1 module osc.message; 2 import osc.oscstring; 3 import osc.typetagstring; 4 import osc.addresspattern; 5 6 AddressPattern toAddressPattern(in ubyte[] b){ 7 import std.algorithm; 8 import std.conv; 9 import std.array; 10 string[] parts = b.map!(c => c.to!char) 11 .to!string[1..$] 12 .replace("\0", "") 13 .split("/"); 14 return parts.map!(p => AddressPart(p)) 15 .array; 16 } 17 unittest{ 18 const ubyte[] b = [0x2f, 0x66, 0x6f, 0x6f, 0x0, 0x0, 0x0, 0x0]; 19 import std.algorithm; 20 import std.stdio; 21 assert(b.toAddressPattern == [AddressPart("foo")]); 22 } 23 /++ 24 +/ 25 struct Message { 26 private { 27 bool isEmptyMessage(ref const(ubyte)[] data) { 28 foreach(i, b; data) { 29 if (i == 0 && b == 44) continue; 30 if (b != 0) return false; 31 } 32 return true; 33 } 34 } 35 public{ 36 /// 37 this(in ubyte[] message){ 38 import std.algorithm; 39 const(ubyte)[] addressPattern = message[0..message.countUntil(0)]; 40 _addressPattern = addressPattern.toAddressPattern; 41 const(ubyte)[] remaining = message[message.countUntil(0)..$].find!"a!=0"; 42 43 assert(remaining.length%4 == 0); 44 if (isEmptyMessage(remaining)) { 45 return; 46 } 47 48 const(ubyte)[] typeTagString = remaining[1..remaining.countUntil(0)]; 49 _typeTagString = TypeTagString(typeTagString); 50 import std.conv; 51 52 remaining = remaining[remaining.countUntil(0)/4*4+4..$]; 53 54 assert(remaining.length%4 == 0); 55 56 foreach (ref c; _typeTagString.content) { 57 switch (c) { 58 case 'i': 59 _args ~= remaining[0..4].dup; 60 remaining = remaining[4..$]; 61 break; 62 case 'f': 63 _args ~= remaining[0..4].dup; 64 remaining = remaining[4..$]; 65 break; 66 case 's': 67 _args ~= remaining[0..remaining.countUntil(0)/4*4+4].dup; 68 remaining = remaining[remaining.countUntil(0)/4*4+4..$]; 69 break; 70 case 'b': 71 _args ~= remaining[0..4].dup; 72 remaining = remaining[4..$]; 73 break; 74 default: 75 assert(0); 76 } 77 } 78 } 79 80 unittest{ 81 ubyte[] buffer = [0x2f, 0x66, 0x6f, 0x6f, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x69, 0x69, 0x73, 0x66, 0x66, 0x0, 0x0, 0x0, 0x0, 0x3, 0xe8, 0xff, 0xff, 0xff, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x0, 0x0, 0x3f, 0x9d, 0xf3, 0xb6, 0x40, 0xb5, 0xb2, 0x2d]; 82 auto message = Message(buffer); 83 } 84 85 /// 86 T opCast(T:ubyte[])()const{ 87 return _opCast!(T)(); 88 } 89 90 /// 91 string toString()const 92 in{ 93 assert(_args.length > 0); 94 }body{ 95 return _opCast!string(); 96 } 97 98 unittest{ 99 auto message = Message(); 100 message._addressPattern = [AddressPart("foo")]; 101 message._typeTagString = TypeTagString("s"); 102 import std.conv; 103 message._args = [OscString("hoge").to!(ubyte[])]; 104 assert(message.to!string == "/foo,shoge"); 105 } 106 107 /// 108 const(AddressPattern) addressPattern()const{ 109 return _addressPattern; 110 } 111 112 /// 113 const(TypeTagString) typeTagString()const{ 114 return _typeTagString; 115 } 116 117 /// 118 TypeTag[] typeTags()const{ 119 import std.algorithm; 120 import std.conv; 121 import std.array; 122 if (_typeTagString.isEmpty) return []; 123 return _typeTagString.content.map!(c => c.to!TypeTag).array; 124 } 125 126 /// 127 T arg(T:ubyte[])(in size_t index)const{ 128 return _args[index].dup; 129 } 130 131 /// 132 T arg(T:int)(in size_t index)const{ 133 import std.bitmanip; 134 return _args[index].peek!T(); 135 } 136 137 /// 138 T arg(T:float)(in size_t index)const{ 139 import std.bitmanip; 140 return _args[index].peek!T(); 141 } 142 143 /// 144 T arg(T:string)(in size_t index)const{ 145 import std.conv:to; 146 import std.algorithm; 147 return _args[index].stripRight(0).map!(c => c.to!char).to!string; 148 } 149 150 /// 151 void addressPattern(in string str){ 152 import std.array; 153 import std.algorithm; 154 _addressPattern = str.split("/") 155 .filter!(a => a != "") 156 .map!(pattern => AddressPart(pattern)) 157 .array; 158 } 159 unittest{ 160 auto message = Message(); 161 message.addressPattern = "/foo/bar"; 162 import std.stdio; 163 import std.conv; 164 assert(message._addressPattern == [AddressPart("foo"), AddressPart("bar")]); 165 } 166 167 /// 168 void addressPattern(AddressPattern p){ 169 import std.array; 170 import std.algorithm; 171 _addressPattern = p; 172 } 173 174 /// 175 void addValue(T)(T v){ 176 import std.conv; 177 char c; 178 static if(is(T == int)){ 179 import std.bitmanip; 180 ubyte[] buffer = [0, 0, 0, 0]; 181 buffer.write!T(v, 0); 182 _args ~= buffer; 183 c = 'i'; 184 }else static if(is(T == float)){ 185 import std.bitmanip; 186 ubyte[] buffer = [0, 0, 0, 0]; 187 buffer.write!T(v, 0); 188 _args ~= buffer; 189 c = 'f'; 190 }else static if(is(T == string)){ 191 _args ~= OscString(v).to!(ubyte[]); 192 c = 's'; 193 }else static if(is(T == ubyte[])){ 194 _args ~= v; 195 c = 'b'; 196 } 197 198 _typeTagString.add(c); 199 } 200 201 /// 202 size_t size()const{ 203 import std.array; 204 return _addressPattern.size + 205 _typeTagString.size + 206 _args.join.length; 207 } 208 unittest{ 209 auto message = Message(); 210 message.addressPattern = [AddressPart("foo")]; 211 message.addValue(1000); 212 message.addValue(-1); 213 message.addValue("hello"); 214 message.addValue(1.234f); 215 message.addValue(5.678f); 216 assert(message.size == 40); 217 } 218 }//public 219 220 private{ 221 AddressPattern _addressPattern; 222 TypeTagString _typeTagString; 223 ubyte[][] _args; 224 225 T _opCast(T)()const{ 226 T b = [0, 0, 0, 0]; 227 import std.conv; 228 import std.array; 229 import std.algorithm; 230 231 auto casterArgs = _args.map!(b => b.convert!T).join; 232 auto seed = AddressPart(); 233 return _addressPattern.fold!((a, b)=> a~b)(seed) 234 .to!(T).dup 235 ~ _typeTagString.to!(T) 236 ~casterArgs; 237 } 238 239 }//private 240 }//struct Message 241 242 243 private T convert(T:ubyte[])(in ubyte[] b){ 244 return b.dup; 245 } 246 private T convert(T:string)(in ubyte[] b){ 247 import std.conv; 248 return OscString!('\0')(b).content; 249 } 250 251 unittest{ 252 auto message = Message(); 253 message.addressPattern = [AddressPart("foo")]; 254 message.addValue(1000); 255 message.addValue(-1); 256 message.addValue("hello"); 257 message.addValue(1.234f); 258 message.addValue(5.678f); 259 260 import std.conv; 261 ubyte[] a = [0x2f, 0x66, 0x6f, 0x6f, 0x0, 0x0, 0x0, 0x0, 0x2c, 0x69, 0x69, 0x73, 0x66, 0x66, 0x0, 0x0, 0x0, 0x0, 0x3, 0xe8, 0xff, 0xff, 0xff, 0xff, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x0, 0x0, 0x3f, 0x9d, 0xf3, 0xb6, 0x40, 0xb5, 0xb2, 0x2d]; 262 assert(message.to!(ubyte[]) == a); 263 } 264 265 unittest{ 266 auto ans = Message(); 267 ans.addressPattern = [AddressPart("foo")]; 268 ans.addValue(1000); 269 ans.addValue(-1); 270 ans.addValue("hello"); 271 ans.addValue(1.234f); 272 ans.addValue(5.678f); 273 274 ubyte[] a = [0x2f, 0x66, 0x6f, 0x6f, 275 0x0, 0x0, 0x0, 0x0, 276 0x2c, 0x69, 0x69, 0x73, 277 0x66, 0x66, 0x0, 0x0, 278 0x0, 0x0, 0x3, 0xe8, 279 0xff, 0xff, 0xff, 0xff, 280 0x68, 0x65, 0x6c, 0x6c, 281 0x6f, 0x0, 0x0, 0x0, 282 0x3f, 0x9d, 0xf3, 0xb6, 283 0x40, 0xb5, 0xb2, 0x2d]; 284 auto message = Message(a); 285 assert(message == ans); 286 }