1 module osc.oscstring; 2 3 /// 4 T addNullSuffix(T:string)(T str){ 5 size_t nullCharacters = 4-str.length%4; 6 if(nullCharacters == 0)return str; 7 import std.range; 8 import std.conv; 9 import std.algorithm; 10 return str ~ ('\0'.repeat(nullCharacters).array).map!(c => cast(immutable(char))c).array; 11 } 12 13 /// 14 T addNullSuffix(T:ubyte[])(T str){ 15 return cast(T)addNullSuffix(cast(string)str); 16 } 17 18 unittest{ 19 assert("osc".addNullSuffix == "osc\0"); 20 assert("data".addNullSuffix == "data\0\0\0\0"); 21 assert(",iisff".addNullSuffix == ",iisff\0\0"); 22 assert(",i".addNullSuffix == ",i\0\0"); 23 } 24 25 /++ 26 +/ 27 struct OscString(char P){ 28 public{ 29 /// 30 enum Prefix = P; 31 32 /// 33 this(in int v){ 34 import std.bitmanip; 35 ubyte[] buffer = [0, 0, 0, 0]; 36 buffer.write!int(v, 0); 37 this(buffer); 38 } 39 40 /// 41 this(in float v){ 42 import std.bitmanip; 43 ubyte[] buffer = [0, 0, 0, 0]; 44 buffer.write!float(v, 0); 45 this(buffer); 46 } 47 48 /// 49 this(in string str) 50 in{ 51 import std.algorithm; 52 assert(!str.canFind("\0")); 53 assert(str != ""); 54 }out{ 55 assert(_data.length%4 == 0); 56 }body{ 57 import std.conv; 58 import std.algorithm; 59 import std.array; 60 ubyte[] arr = str.map!(c => c.to!char.to!ubyte).array; 61 this(arr); 62 } 63 unittest{ 64 import core.exception, std.exception; 65 assertThrown!AssertError(OscString("\0string\0mixed\0null\0")); 66 assertThrown!AssertError(OscString("")); 67 } 68 69 /// 70 this(in ubyte[] arr) 71 in{ 72 import std.algorithm; 73 // assert(!arr.canFind(null)); 74 assert(arr.length > 0); 75 }body{ 76 if(Prefix != '\0'){ 77 import std.conv; 78 _data = Prefix.to!ubyte ~ _data; 79 } 80 81 foreach (ref c; arr) { 82 _data ~= c; 83 } 84 _data = _data.addNullSuffix; 85 } 86 /// 87 string toString()const{ 88 import std.conv:to; 89 import std.algorithm; 90 return _data.stripRight(0).map!(c => c.to!char).to!string; 91 } 92 93 unittest{ 94 ubyte[] buffer = [0x66, 0x6f, 0x6f]; 95 import std.stdio; 96 import std.conv; 97 assert(OscString!('\0')(buffer).to!string == "foo"); 98 assert(OscString!('\0')(buffer).size == 4); 99 } 100 101 unittest{ 102 auto oscString = OscString!('\0')("osc"); 103 import std.stdio; 104 import std.conv; 105 assert(oscString.to!string == "osc"); 106 } 107 108 unittest{ 109 import std.conv; 110 auto oscString = OscString!('\0')("data"); 111 assert(oscString.to!string == "data"); 112 } 113 114 /// 115 ubyte[] opCast(T:ubyte[])()const{ 116 return _data.dup; 117 } 118 119 /// 120 T opCast(T:int)()const if(Prefix == '\0'){ 121 import std.bitmanip; 122 return _data.peek!T(); 123 } 124 125 /// 126 T opCast(T:float)()const if(Prefix == '\0'){ 127 import std.bitmanip; 128 return _data.peek!T(); 129 } 130 131 T opBinary(string op:"~", T)(in T rhs)const{ 132 import std.algorithm; 133 import std.conv; 134 ubyte[] stripedLhsData = this._data.dup.stripRight(0); 135 ubyte[] stripedRhsData = rhs._data.dup.stripRight(0); 136 T result; 137 result._data = (stripedLhsData ~ stripedRhsData).addNullSuffix; 138 return result; 139 } 140 141 unittest{ 142 const oscStringLhs = OscString!('/')("foo"); 143 const oscStringRhs = OscString!('/')("barbaz"); 144 import std.stdio; 145 import std.conv; 146 assert((oscStringLhs ~ oscStringRhs).to!string == "/foo/barbaz"); 147 } 148 // T convert(T)(in AddressPattern pattern){ 149 // string joinedString = pattern.map!(p => p.to!string).reduce!((a, b)=>a~b); 150 // return OscString; 151 // } 152 153 /// 154 bool isEmpty()const{ 155 import std.algorithm; 156 import std.conv; 157 ubyte prefix = P.to!ubyte; 158 if(prefix != 0){ 159 return _data.length == 0; 160 }else{ 161 return _data.stripLeft(prefix).length == 0; 162 } 163 } 164 165 /// 166 size_t size()const{ 167 return _data.length; 168 } 169 170 unittest{ 171 import std.conv; 172 auto oscString = OscString!('\0')("data"); 173 assert(oscString.size == 8); 174 } 175 176 }//public 177 178 private{ 179 ubyte[] _data; 180 }//private 181 }//struct OscString 182 183 184 unittest{ 185 const ubyte[] buffer = [0x00, 0x00, 0x6f, 0x00]; 186 import std.conv; 187 assert(OscString!('\0')(buffer).to!int == 28416); 188 } 189 190 unittest{ 191 const ubyte[] buffer = [0x3f, 0x9d, 0xf3, 0xb6]; 192 import std.conv; 193 import std.math; 194 assert(approxEqual(OscString!('\0')(buffer).to!float, 1.234)); 195 } 196 197 /// 198 OscString!('\0') OscString(T)(in T v){ 199 return OscString!('\0')(v); 200 } 201 202 /// 203 template isOscString(S){ 204 enum bool isOscString = __traits(compiles, (){ 205 S oscString = OscString!(S.Prefix)(); 206 }); 207 } 208 209 unittest{ 210 static assert(isOscString!(OscString!('a'))); 211 static assert(isOscString!(OscString!('\n'))); 212 static assert(!isOscString!(string)); 213 } 214 215 /// 216 string content(S)(in S oscString)if(isOscString!(S)){ 217 import std.string; 218 import std.conv; 219 string str = oscString.to!string.replace("\0", ""); 220 if(S.Prefix != '\0'){ 221 return str[1..$]; 222 }else{ 223 return str; 224 } 225 // return oscString._data 226 } 227 228 unittest{ 229 import std.string; 230 assert(OscString!('\0')("data").content == "data"); 231 assert(OscString!('/')("data").content == "data"); 232 }