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