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 }