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 }