-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmodule.pmod
445 lines (398 loc) · 13.1 KB
/
module.pmod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
//! A module for working with the BSON format.
//!
private int counter;
constant TYPE_FLOAT = 0x01;
constant TYPE_STRING = 0x02;
constant TYPE_DOCUMENT = 0x03;
constant TYPE_ARRAY = 0x04;
constant TYPE_BINARY = 0x05;
constant TYPE_OBJECTID = 0x07;
constant TYPE_BOOLEAN = 0x08;
constant TYPE_DATETIME = 0x09;
constant TYPE_NULL = 0x0a;
constant TYPE_REGEX = 0x0b;
constant TYPE_INT32 = 0x10;
constant TYPE_INT64 = 0x12;
constant TYPE_MIN_KEY = 0xff;
constant TYPE_MAX_KEY = 0x7f;
constant TYPE_SYMBOL = 0x0e;
constant TYPE_JAVASCRIPT = 0x0D;
constant TYPE_TIMESTAMP = 0x11;
/* TODO: still need to support the following types:
| "\x0F" e_name code_w_s JavaScript code w/ scope
*/
// binary data subtypes
constant BINARY_GENERIC = 0x00;
constant BINARY_FUNCTION = 0x01;
constant BINARY_OLD = 0x02;
constant BINARY_UUID = 0x03;
constant BINARY_MD5 = 0x05;
constant BINARY_USER = 0x80;
int getCounter()
{
return ++counter;
}
//!
//! @param query_mode
//! if set to true, encoding will allow "$" and "." in key names, which
//! would normally be disallowed.
string toDocument(mapping m, int|void query_mode)
{
String.Buffer buf = String.Buffer();
encode(m, buf, query_mode);
return sprintf("%-4c%s%c", sizeof(buf)+5, buf->get(), 0);
}
static string toCString(string str)
{
if(search(str, "\0") != -1) throw(Error.Generic("String cannot contain null bytes.\n"));
else return string_to_utf8(str) + "\0";
}
static void encode(mixed m, String.Buffer buf, int|void allow_specials)
{
foreach(m; mixed key; mixed val)
{
if(!stringp(key)) throw(Error.Generic("BSON Keys must be strings.\n"));
if(search(key, "\0") != -1) throw(Error.Generic("BSON Keys may not contain NULL characters.\n"));
if(!allow_specials && ((key - "$") - ".") != key)
throw(Error.Generic("BSON keys may not contain '$' or '.' characters unless in query-mode.\n"));
key = string_to_utf8(key);
encode_value(key, val, buf, allow_specials);
}
}
static void encode_value(string key, mixed value, String.Buffer buf, int|void allow_specials)
{
if(floatp(value))
{
buf->add(sprintf("%c%s%c%-8F", TYPE_FLOAT, key, 0, value));
}
else if(stringp(value))
{
string v = string_to_utf8(value);
buf->add(sprintf("%c%s%c%-4c%s%c", TYPE_STRING, key, 0, sizeof(v)+1, v, 0));
}
else if(mappingp(value))
{
buf->add(sprintf("%c%s%c%s", TYPE_DOCUMENT, key, 0, toDocument(value, allow_specials)));
}
else if(arrayp(value))
{
int qi = 0;
buf->add(sprintf("%c%s%c%s", TYPE_ARRAY, key, 0, toDocument(mkmapping(map(value, lambda(mixed e){return (string)qi++;}), value), allow_specials)));
}
else if(intp(value))
{
// werror("have int\n");
// 32 bit or 64 bit?
if(value <= 2147383647 && value >= -2148483648) // we fit in a 32 bit space.
{
// werror("32bit\n");
buf->add(sprintf("%c%s%c%-4c", TYPE_INT32, key, 0, value));
}
else
{
// werror("64bit\n");
buf->add(sprintf("%c%s%c%-8c", TYPE_INT64, key, 0, value));
}
}
// Calendar instance
else if(objectp(value) && value->unix_time && value->utc_offset) // a date object
{
buf->add(sprintf("%c%s%c%-8c", TYPE_DATETIME, key, 0, (value->unix_time() /* + value->utc_offset() */ * 1000)));
}
// BSON.ObjectId instance
else if(objectp(value) && Program.inherits(object_program(value), BSON.ObjectId))
{
buf->add(sprintf("%c%s%c%12s", TYPE_OBJECTID, key, 0, value->get_id()));
}
// BSON.Timestamp instance
else if(objectp(value) && Program.inherits(object_program(value), BSON.Timestamp))
{
buf->add(sprintf("%c%s%c%-8s", TYPE_TIMESTAMP, key, 0, value->get_timestamp()));
}
// BSON.Binary instance
else if(objectp(value) && Program.inherits(object_program(value), BSON.Binary))
{
buf->add(sprintf("%c%s%c%-4c%c%s", TYPE_BINARY, key, 0, sizeof(value)+1, value->subtype, (string)value));
}
// BSON.Symbol instance
else if(objectp(value) && Program.inherits(object_program(value), BSON.Symbol))
{
string v = (string)value;
v = string_to_utf8(v);
buf->add(sprintf("%c%s%c%-4c%s%c", TYPE_SYMBOL, key, 0, sizeof(v)+1, v, 0));
}
// BSON.Javascript instance
else if(objectp(value) && Program.inherits(object_program(value), BSON.Javascript))
{
string v = (string)value;
v = string_to_utf8(v);
buf->add(sprintf("%c%s%c%-4c%s%c", TYPE_JAVASCRIPT, key, 0, sizeof(v)+1, v, 0));
}
// BSON.Regex instance
else if(objectp(value) && Program.inherits(object_program(value), BSON.Regex))
{
string v = (string)value;
v = string_to_utf8(v);
buf->add(sprintf("%c%s%c%s%s", TYPE_REGEX, key, 0, toCString(value->regex), toCString(value->options)));
}
// BSON.Null
else if(objectp(value) && value == Null)
{
buf->add(sprintf("%c%s%c", TYPE_NULL, key, 0));
}
// BSON.True
else if(objectp(value) && value == True)
{
buf->add(sprintf("%c%s%c%c", TYPE_BOOLEAN, key, 0, 1));
}
// BSON.False
else if(objectp(value) && value == False)
{
buf->add(sprintf("%c%s%c%c", TYPE_BOOLEAN, key, 0, 0));
}
// BSON.MinKey
else if(objectp(value) && value->BSONMaxKey)
{
buf->add(sprintf("%c%s%c", TYPE_MIN_KEY, key, 0));
}
// BSON.MaxKey
else if(objectp(value) && value->BSONMaxKey)
{
buf->add(sprintf("%c%s%c", TYPE_MAX_KEY, key, 0));
}
//werror("bufsize: %O\n", sizeof(buf));
}
//!
mixed fromDocument(string bson)
{
int len;
string slist;
if(sscanf(bson, "%-4c%s", len, bson)!=2)
throw(Error.Generic("Unable to read length from BSON stream.\n"));
if(sizeof(bson) < (len -4))
throw(Error.Generic(sprintf("Unable to read full data from BSON stream, expected %d, got %d.\n", len-4, sizeof(bson)-1)));
slist = bson[0..<1 ];
//werror("bson length %d\n", len);
mapping list = ([]);
do
{
slist = decode_next_value(slist, list);
werror("sizeof: %O\n", sizeof(slist));
//werror("read item: %O, left: %O", list, slist);
} while(sizeof(slist));
return list;
}
string toDocumentArray(array documents)
{
String.Buffer buf = String.Buffer();
foreach(documents;;mixed document)
{
buf->add(toDocument(document));
}
return buf->get();
}
array fromDocumentArray(string bsonarray)
{
array a = ({});
while(sizeof(bsonarray))
{
string bson;
int len;
if(sscanf(bsonarray, "%-4c", len)!=1)
throw(Error.Generic("Unable to read length from BSON stream.\n"));
if(sscanf(bsonarray, "%" + len + "s%s", bson, bsonarray) != 2)
throw(Error.Generic("Unable to read full data from BSON stream.\n"));
// werror("parsing BSON: %O\n", bson);
a+=({fromDocument(bson)});
// werror("bsonarray: %O\n", bsonarray);
}
// werror("done parsing.\n");
return a;
}
static string decode_next_value(string slist, mapping list)
{
string key;
string values;
mixed value;
int type;
string document;
int doclen;
if(sscanf(slist, "%c%s\0%s", type, key, slist)!=3)
throw(Error.Generic("Unable to read key and type from BSON stream.\n"));
key = utf8_to_string(key);
werror("key: %s type: %d\n", key, type);
switch(type)
{
int len, subtype;
case TYPE_FLOAT:
if(sscanf(slist, "%-8s%s", value, slist) != 2)
throw(Error.Generic("Unable to read float from BSON stream.\n"));
if(sscanf(reverse(value), "%8F", value) != 1 )
throw(Error.Generic("Unable to read float from BSON stream.\n"));
break;
case TYPE_STRING:
if(sscanf(slist, "%-4c%s", len, slist) != 2)
throw(Error.Generic("Unable to read string length from BSON stream.\n"));
if(sscanf(slist, "%" + (len-1) + "s\0%s", value, slist) != 2)
throw(Error.Generic("Unable to read string from BSON stream.\n"));
value = utf8_to_string(value);
break;
case TYPE_BINARY:
if(sscanf(slist, "%-4c%s", len, slist) != 2)
throw(Error.Generic("Unable to read binary length from BSON stream.\n"));
if(sscanf(slist, "%c%" + (len-1) + "s\0%s", subtype, value, slist) != 2)
throw(Error.Generic("Unable to read binary from BSON stream.\n"));
value = .Binary(value, subtype);
break;
case TYPE_JAVASCRIPT:
if(sscanf(slist, "%-4c%s", len, slist) != 2)
throw(Error.Generic("Unable to read javascript length from BSON stream.\n"));
if(sscanf(slist, "%" + (len-1) + "s\0%s", value, slist) != 2)
throw(Error.Generic("Unable to read javascript from BSON stream.\n"));
value = .Javascript(utf8_to_string(value));
break;
case TYPE_SYMBOL:
if(sscanf(slist, "%-4c%s", len, slist) != 2)
throw(Error.Generic("Unable to read symbol length from BSON stream.\n"));
if(sscanf(slist, "%" + (len-1) + "s\0%s", value, slist) != 2)
throw(Error.Generic("Unable to read symbol from BSON stream.\n"));
value = .Symbol(utf8_to_string(value));
break;
case TYPE_REGEX:
string regex, options;
if(sscanf(slist, "%s\0%s", regex, slist)!=2)
throw(Error.Generic("Unable to read regex from BSON stream.\n"));
regex = utf8_to_string(regex);
if(sscanf(slist, "%s\0%s", options, slist)!=2)
throw(Error.Generic("Unable to read regex options from BSON stream.\n"));
options = utf8_to_string(options);
value = .Regex(regex, options);
break;
case TYPE_INT32:
if(sscanf(slist, "%-4c%s", value, slist) != 2)
throw(Error.Generic("Unable to read int32 from BSON stream.\n"));
break;
case TYPE_INT64:
if(sscanf(slist, "%-8c%s", value, slist) != 2)
throw(Error.Generic("Unable to read int64 from BSON stream.\n"));
break;
case TYPE_OBJECTID:
if(sscanf(slist, "%12s%s", value, slist) != 2)
throw(Error.Generic("Unable to read object id from BSON stream.\n"));
value = .ObjectId(value);
break;
case TYPE_TIMESTAMP:
if(sscanf(slist, "%-12c%s", value, slist) != 2)
throw(Error.Generic("Unable to read timestamp from BSON stream.\n"));
value = .Timestamp(value);
break;
case TYPE_BOOLEAN:
if(sscanf(slist, "%c%s", value, slist) != 2)
throw(Error.Generic("Unable to read boolean from BSON stream.\n"));
if(value) value = True;
else value = False;
break;
case TYPE_NULL:
value = Null;
break;
case TYPE_MIN_KEY:
value = MinKey;
break;
case TYPE_MAX_KEY:
value = MaxKey;
break;
case TYPE_DATETIME:
if(sscanf(slist, "%-8c%s", value, slist) != 2)
throw(Error.Generic("Unable to read datetime from BSON stream.\n"));
value/=1000;
value = Calendar.Second("unix", value);
break;
case TYPE_DOCUMENT:
if(sscanf(slist, "%-4c", doclen) != 1)
throw(Error.Generic("Unable to read embedded document length\n"));
if(!sscanf(slist, "%" + (doclen) + "s%s", document, slist))
throw(Error.Generic("Unable to read specified length for embedded document.\n"));
werror("document: %O\n", document);
value = fromDocument(document);
break;
case TYPE_ARRAY:
if(sscanf(slist, "%-4c", doclen) != 1)
throw(Error.Generic("Unable to read embedded document length\n"));
if(sscanf(slist, "%" + (doclen) + "s%s", document, slist) !=2)
throw(Error.Generic("Unable to read specified length for embedded document.\n"));
value = fromDocument(document);
int asize = sizeof(value);
array bval = allocate(asize);
for(int i = 0; i < asize; i++)
{
bval[i] = value[(string)i];
}
value=bval;
// value=predef::values(value);
break;
default:
throw(Error.Generic("Unknown BSON type " + type + ".\n"));
}
list[key] = value;
// werror("type: %d key %s\n", type, key);
return slist;
}
//!
object Null = null();
//!
object True = true_object();
//!
object False = false_object();
//!
object MinKey = minkey_object();
//!
object MaxKey = maxkey_object();
class false_object
{
constant BSONFalse = 1;
static mixed cast(string type)
{
if(type == "string")
return "false";
if(type == "int")
return 0;
}
}
class true_object
{
constant BSONTrue = 1;
static mixed cast(string type)
{
if(type == "string")
return "true";
if(type == "int")
return 1;
}
}
class maxkey_object
{
constant BSONMaxKey = 1;
static mixed cast(string type)
{
if(type == "string")
return "MinKey";
}
}
class minkey_object
{
constant BSONMinKey = 1;
static mixed cast(string type)
{
if(type == "string")
return "MinKey";
}
}
class null
{
constant BSONNull = 1;
static string cast(string type)
{
if(type == "string")
return "null";
}
}