data.cc 24.4 KB
Newer Older
Jelte Jansen's avatar
Jelte Jansen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$
16
17
18
19
20

#include "data.h"

#include <cstdio>
#include <iostream>
21
#include <string>
22
23
#include <sstream>

24
#include <boost/algorithm/string.hpp> // for iequals
25
26

using namespace std;
Jelte Jansen's avatar
Jelte Jansen committed
27
using namespace isc::data;
28
29
30

const unsigned char PROTOCOL_VERSION[4] = { 0x53, 0x6b, 0x61, 0x6e };

31
const unsigned char ITEM_BLOB = 0x01;
32
33
34
const unsigned char ITEM_HASH = 0x02;
const unsigned char ITEM_LIST = 0x03;
const unsigned char ITEM_NULL = 0x04;
Jelte Jansen's avatar
Jelte Jansen committed
35
const unsigned char ITEM_BOOL = 0x05;
36
const unsigned char ITEM_INT  = 0x06;
37
const unsigned char ITEM_REAL = 0x07;
38
const unsigned char ITEM_UTF8 = 0x08;
39
40
41
42
43
44
45
const unsigned char ITEM_MASK = 0x0f;

const unsigned char ITEM_LENGTH_32   = 0x00;
const unsigned char ITEM_LENGTH_16   = 0x10;
const unsigned char ITEM_LENGTH_8    = 0x20;
const unsigned char ITEM_LENGTH_MASK = 0x30;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
46
47
48
49
50
namespace isc {
namespace data {

namespace {
inline void
51
throwParseError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
Jelte Jansen's avatar
Jelte Jansen committed
52
53
54
{
    if (line != 0 || pos != 0) {
        std::stringstream ss;
55
        ss << error << " in " + file + ":" << line << ":" << pos;
Jelte Jansen's avatar
Jelte Jansen committed
56
57
58
59
        throw ParseError(ss.str());
    } else {
        throw ParseError(error);
    }
60
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
61
}
62

Jelte Jansen's avatar
Jelte Jansen committed
63
64
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e) {
    return out << e->str();
65
66
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
67
bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b) {
68
69
70
    return a->equals(b);
};

71
72
73
74
//
// factory functions
//
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
75
Element::create(const int i) {
76
77
78
79
80
81
82
83
    try {
        return ElementPtr(new IntElement(i));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
84
Element::create(const double d) {
85
86
87
88
89
90
91
92
    try {
        return ElementPtr(new DoubleElement(d));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
93
Element::create(const std::string& s) {
94
95
96
97
98
99
100
101
    try {
        return ElementPtr(new StringElement(s));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
102
Element::create(const bool b) {
103
104
105
106
107
108
109
110
    try {
        return ElementPtr(new BoolElement(b));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
111
Element::create(const std::vector<ElementPtr>& v) {
112
113
114
115
116
117
118
119
    try {
        return ElementPtr(new ListElement(v));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
120
Element::create(const std::map<std::string, ElementPtr>& m) {
121
122
123
124
125
126
127
128
129
    try {
        return ElementPtr(new MapElement(m));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}


//
130
// helper functions for createFromString factory
131
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
132
133
namespace {
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
134
135
char_in(const char c, const char *chars) {
    for (size_t i = 0; i < strlen(chars); ++i) {
136
137
138
139
140
141
142
        if (chars[i] == c) {
            return true;
        }
    }
    return false;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
143
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
144
skip_chars(std::istream &in, const char *chars, int& line, int& pos) {
145
146
    char c = in.peek();
    while (char_in(c, chars) && c != EOF) {
147
        if (c == '\n') {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
148
            ++line;
149
150
            pos = 1;
        } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
151
            ++pos;
152
        }
153
154
155
156
157
158
159
        in.get();
        c = in.peek();
    }
}

// skip on the input stream to one of the characters in chars
// if another character is found this function returns false
160
// unless that character is specified in the optional may_skip
161
162
//
// the character found is left on the stream
JINMEI Tatuya's avatar
JINMEI Tatuya committed
163
void
Jelte Jansen's avatar
Jelte Jansen committed
164
165
skip_to(std::istream &in, const std::string& file, int& line,
        int& pos, const char* chars, const char* may_skip="")
166
167
{
    char c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
168
    ++pos;
169
    while (c != EOF) {
170
171
        if (c == '\n') {
            pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
172
            ++line;
173
        }
174
175
        if (char_in(c, may_skip)) {
            c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
176
            ++pos;
177
178
        } else if (char_in(c, chars)) {
            while(char_in(in.peek(), may_skip)) {
179
180
                if (in.peek() == '\n') {
                    pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
181
                    ++line;
182
                }
183
                in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
184
                ++pos;
185
186
            }
            in.putback(c);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
187
            --pos;
188
            return;
189
        } else {
Jelte Jansen's avatar
Jelte Jansen committed
190
            throwParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
191
192
        }
    }
Jelte Jansen's avatar
Jelte Jansen committed
193
    throwParseError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
194
195
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
196
std::string
Jelte Jansen's avatar
Jelte Jansen committed
197
str_from_stringstream(std::istream &in, const std::string& file, int& line, int& pos) throw (ParseError)
198
199
200
201
{
    char c = 0;
    std::stringstream ss;
    c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
202
    ++pos;
203
204
    if (c == '"') {
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
205
        ++pos;
206
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
207
        throwParseError("String expected", file, line, pos);
208
209
210
211
212
    }
    while (c != EOF && c != '"') {
        ss << c;
        if (c == '\\' && in.peek() == '"') {
            ss << in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
213
            ++pos;
214
215
        }
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
216
        ++pos;
217
218
219
220
    }
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
221
std::string
222
word_from_stringstream(std::istream &in, int& line UNUSED_PARAM, int& pos) {
223
224
225
226
    std::stringstream ss;
    while (isalpha(in.peek())) {
        ss << (char) in.get();
    }
227
    pos += ss.str().size();
228
229
230
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
231
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
232
count_chars_i(int i) {
233
234
    int result = 1;
    while (i > 10) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
235
236
        ++result;
        i = i / 10;
237
238
239
240
    }
    return result;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
241
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
242
count_chars_d(double d) {
243
    int result = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
244
245
    while (d < 1.0) {
        ++result;
246
247
248
249
        d = d * 10;
    }
    return result;
}
250

JINMEI Tatuya's avatar
JINMEI Tatuya committed
251
ElementPtr
252
253
254
from_stringstream_int_or_double(std::istream &in, int &line UNUSED_PARAM,
                                int &pos)
{
255
256
    int i;
    in >> i;
257
    pos += count_chars_i(i);
258
259
260
    if (in.peek() == '.') {
        double d;
        in >> d;
261
        pos += count_chars_d(i);
262
263
264
265
266
267
268
        d += i;
        return Element::create(d);
    } else {
        return Element::create(i);
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
269
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
270
from_stringstream_bool(std::istream &in, const std::string& file, int& line, int& pos)
271
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
272
    const std::string word = word_from_stringstream(in, line, pos);
273
274
275
276
277
    if (boost::iequals(word, "True")) {
        return Element::create(true);
    } else if (boost::iequals(word, "False")) {
        return Element::create(false);
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
278
279
280
        throwParseError(std::string("Bad boolean value: ") + word, file, line, pos);
        // above is a throw shortcur, return empty is never reached
        return ElementPtr();
281
282
283
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
284
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
285
from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
286
{
Jelte Jansen's avatar
Jelte Jansen committed
287
    return Element::create(str_from_stringstream(in, file, line, pos));
288
289
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
290
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
291
from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
292
293
294
295
296
{
    char c = 0;
    std::vector<ElementPtr> v;
    ElementPtr cur_list_element;

297
    skip_chars(in, " \t\n", line, pos);
298
    while (c != EOF && c != ']') {
299
        if (in.peek() != ']') {
Jelte Jansen's avatar
Jelte Jansen committed
300
            cur_list_element = Element::createFromString(in, file, line, pos);
301
            v.push_back(cur_list_element);
Jelte Jansen's avatar
Jelte Jansen committed
302
            skip_to(in, file, line, pos, ",]", " \t\n");
303
304
        }
        c = in.get();
305
        pos++;
306
307
308
309
    }
    return Element::create(v);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
310
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
311
from_stringstream_map(std::istream &in, const std::string& file, int& line, int& pos)
312
313
314
315
316
317
{
    char c = 0;
    std::map<std::string, ElementPtr> m;
    std::pair<std::string, ElementPtr> p;
    std::string cur_map_key;
    ElementPtr cur_map_element;
318
    skip_chars(in, " \t\n", line, pos);
319
320
321
    c = in.peek();
    if (c == '}') {
        // empty map, skip closing curly
322
        c = in.get();
323
324
325
326
327
328
329
330
331
332
333
334
335
    } else {
        while (c != EOF && c != '}') {
            p.first = str_from_stringstream(in, file, line, pos);
            skip_to(in, file, line, pos, ":", " \t\n");
            // skip the :
            in.get();
            pos++;
            p.second = Element::createFromString(in, file, line, pos);
            m.insert(p);
            skip_to(in, file, line, pos, ",}", " \t\n");
            c = in.get();
            pos++;
        }
336
337
338
    }
    return Element::create(m);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
339
}
340
341

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
342
Element::createFromString(std::istream& in) throw(ParseError) {
343
    int line = 1, pos = 1;
344
345
346
347
    return createFromString(in, "<istream>", line, pos);
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
348
Element::createFromString(std::istream& in, const std::string& file_name) throw(ParseError)
349
350
351
{
    int line = 1, pos = 1;
    return createFromString(in, file_name, line, pos);
352
353
354
}

ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
355
Element::createFromString(std::istream &in, const std::string& file, int& line, int& pos) throw(ParseError)
356
357
358
359
{
    char c = 0;
    ElementPtr element;
    bool el_read = false;
360
    skip_chars(in, " \n\t", line, pos);
361
362
    while (c != EOF && !el_read) {
        c = in.get();
363
        pos++;
364
365
366
367
368
369
370
371
372
373
374
375
        switch(c) {
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '0':
                in.putback(c);
376
                element = from_stringstream_int_or_double(in, line, pos);
377
378
379
380
381
382
383
                el_read = true;
                break;
            case 't':
            case 'T':
            case 'f':
            case 'F':
                in.putback(c);
Jelte Jansen's avatar
Jelte Jansen committed
384
                element = from_stringstream_bool(in, file, line, pos);
385
386
387
388
                el_read = true;
                break;
            case '"':
                in.putback('"');
Jelte Jansen's avatar
Jelte Jansen committed
389
                element = from_stringstream_string(in, file, line, pos);
390
391
392
                el_read = true;
                break;
            case '[':
Jelte Jansen's avatar
Jelte Jansen committed
393
                element = from_stringstream_list(in, file, line, pos);
394
395
396
                el_read = true;
                break;
            case '{':
Jelte Jansen's avatar
Jelte Jansen committed
397
                element = from_stringstream_map(in, file, line, pos);
398
399
                el_read = true;
                break;
400
401
            case EOF:
                break;
402
            default:
Jelte Jansen's avatar
Jelte Jansen committed
403
                throwParseError(std::string("error: unexpected character ") + c, file, line, pos);
404
405
406
407
408
409
                break;
        }
    }
    if (el_read) {
        return element;
    } else {
410
        throw ParseError("nothing read");
411
412
413
    }
}

414
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
415
Element::createFromString(const std::string &in) {
416
417
    std::stringstream ss;
    ss << in;
418
    return createFromString(ss, "<string>");
419
}
420

421
422
423
424
//
// a general to_str() function
//
std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
425
IntElement::str() {
426
    std::stringstream ss;
427
    ss << intValue();
428
429
430
431
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
432
DoubleElement::str() {
433
    std::stringstream ss;
434
    ss << doubleValue();
435
436
437
438
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
439
BoolElement::str() {
440
441
442
443
444
445
446
447
    if (b) {
        return "True";
    } else {
        return "False";
    }
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
448
StringElement::str() {
449
450
    std::stringstream ss;
    ss << "\"";
451
    ss << stringValue();
452
453
454
455
456
    ss << "\"";
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
457
ListElement::str() {
458
459
    std::stringstream ss;
    ss << "[ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
460
461
462
463

    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin();
         it != v.end(); ++it) {
464
465
466
467
468
469
470
471
472
473
        if (it != v.begin()) {
            ss << ", ";
        }
        ss << (*it)->str();
    }
    ss << " ]";
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
474
MapElement::str() {
475
476
    std::stringstream ss;
    ss << "{";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
477
478
479
480

    const std::map<std::string, ElementPtr>& m = mapValue();
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
         it != m.end(); ++it) {
481
482
483
484
        if (it != m.begin()) {
            ss << ", ";
        }
        ss << "\"" << (*it).first << "\": ";
485
486
487
488
489
        if ((*it).second) {
            ss << (*it).second->str();
        } else {
            ss << "None";
        }
490
491
492
493
494
    }
    ss << "}";
    return ss.str();
}

495
// throws when one of the types in the path (except the one
496
497
498
499
// we're looking for) is not a MapElement
// returns 0 if it could simply not be found
// should that also be an exception?
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
500
501
MapElement::find(const std::string& id) {
    const size_t sep = id.find('/');
502
503
504
505
506
    if (sep == std::string::npos) {
        return get(id);
    } else {
        ElementPtr ce = get(id.substr(0, sep));
        if (ce) {
507
            // ignore trailing slash
JINMEI Tatuya's avatar
JINMEI Tatuya committed
508
509
            if  (sep + 1 != id.size()) {
                return ce->find(id.substr(sep + 1));
510
511
512
            } else {
                return ce;
            }
513
514
515
516
517
518
519
520
521
        } else {
            return ElementPtr();
        }
    }
}

//
// Decode from wire format.
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
522
namespace {
523
524
ElementPtr decode_element(std::stringstream& in, int& in_length);

JINMEI Tatuya's avatar
JINMEI Tatuya committed
525
unsigned char
JINMEI Tatuya's avatar
JINMEI Tatuya committed
526
527
get_byte(std::stringstream& in) {
    const int c = in.get();
528
529
530
531
532
533
534
535
    if (c == EOF) {
        throw DecodeError("End of data while decoding wire format message");
    }

    return c;
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
536
decode_tag(std::stringstream& in, int& item_length) {
537
538
    char buf[256];

JINMEI Tatuya's avatar
JINMEI Tatuya committed
539
    const int len = get_byte(in);
540
541
542
543
544
545
546
547
548
549
550
551
    item_length--;

    in.read(buf, len);
    if (in.fail()) {
        throw DecodeError();
    }
    buf[len] = 0;
    item_length -= len;

    return std::string(buf, len);
}

Jelte Jansen's avatar
Jelte Jansen committed
552
ElementPtr
553
decode_bool(std::stringstream& in, int& item_length UNUSED_PARAM) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
554
    const char c = in.get();
555
556
    
    if (c == '1') {
Jelte Jansen's avatar
Jelte Jansen committed
557
558
559
560
561
562
        return Element::create(true);
    } else {
        return Element::create(false);
    }
}

563
ElementPtr
564
decode_int(std::stringstream& in, int& item_length UNUSED_PARAM) {
565
566
    int skip, me;
    return from_stringstream_int_or_double(in, skip, me);
567
568
}

569
ElementPtr
570
decode_real(std::stringstream& in, int& item_length UNUSED_PARAM) {
571
572
573
574
    int skip, me;
    return from_stringstream_int_or_double(in, skip, me);
}

575
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
576
decode_blob(std::stringstream& in, int& item_length) {
577
578
579
580
    char *buf = new char[item_length + 1];

    in.read(buf, item_length);
    if (in.fail()) {
581
        delete[] buf;
582
583
584
585
586
587
588
589
590
591
592
593
594
        throw DecodeError();
    }
    buf[item_length] = 0;

    std::string s = std::string(buf, item_length);
    item_length -= item_length;

    delete [] buf;
    return Element::create(s);
}

// XXXMLG currently identical to decode_blob
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
595
decode_utf8(std::stringstream& in, int& item_length) {
596
597
598
599
    char *buf = new char[item_length + 1];

    in.read(buf, item_length);
    if (in.fail()) {
600
        delete[] buf;
601
602
603
604
605
606
607
608
609
610
611
612
        throw DecodeError();
    }
    buf[item_length] = 0;

    std::string s = std::string(buf, item_length);
    item_length -= item_length;

    delete [] buf;
    return Element::create(s);
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
613
decode_hash(std::stringstream& in, int& item_length) {
614
615
616
617
618
619
620
621
622
623
624
625
626
    std::map<std::string, ElementPtr> m;
    std::pair<std::string, ElementPtr> p;

    while (item_length > 0) {
        p.first = decode_tag(in, item_length);
        p.second = decode_element(in, item_length);
        m.insert(p);
    }

    return Element::create(m);
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
627
decode_list(std::stringstream& in, int& item_length) {
628
629
630
631
632
633
634
635
636
    std::vector<ElementPtr> v;

    while (item_length > 0) {
        v.push_back(decode_element(in, item_length));
    }
    return Element::create(v);
}

ElementPtr
637
decode_null(std::stringstream& in UNUSED_PARAM, int& item_length UNUSED_PARAM) {
638
639
640
641
    return Element::create("NULL");
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
642
decode_element(std::stringstream& in, int& in_length) {
643
644
    ElementPtr element;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
645
646
647
    const unsigned char type_and_length = get_byte(in);
    const unsigned char type = type_and_length & ITEM_MASK;
    const unsigned char lenbytes = type_and_length & ITEM_LENGTH_MASK;
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
    in_length--;

    int item_length = 0;
    switch (lenbytes) {
    case ITEM_LENGTH_32:
        item_length |= get_byte(in);
        item_length <<= 8;
        item_length |= get_byte(in);
        item_length <<= 8;
        in_length -= 2;  // only 2 here, we will get more later
    case ITEM_LENGTH_16:
        item_length |= get_byte(in);
        item_length <<= 8;
        in_length--;  // only 1 here
    case ITEM_LENGTH_8:
        item_length |= get_byte(in);
        in_length--;
    }

    in_length -= item_length;

    switch (type) {
Jelte Jansen's avatar
Jelte Jansen committed
670
671
672
    case ITEM_BOOL:
        element = decode_bool(in, item_length);
        break;
673
674
675
    case ITEM_INT:
        element = decode_int(in, item_length);
        break;
676
677
678
    case ITEM_REAL:
        element = decode_real(in, item_length);
        break;
679
680
681
682
683
    case ITEM_BLOB:
        element = decode_blob(in, item_length);
        break;
    case ITEM_UTF8:
        element = decode_utf8(in, item_length);
684
685
686
687
688
689
690
691
692
693
694
695
696
697
        break;
    case ITEM_HASH:
        element = decode_hash(in, item_length);
        break;
    case ITEM_LIST:
        element = decode_list(in, item_length);
        break;
    case ITEM_NULL:
        element = decode_null(in, item_length);
        break;
    }

    return (element);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
698
}
699
700

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
701
Element::fromWire(const std::string& s) {
702
703
    std::stringstream ss;
    ss << s;
704
    return fromWire(ss, s.length());
705
706
707
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
708
Element::fromWire(std::stringstream& in, int length) {
709
710
711
    //
    // Check protocol version
    //
JINMEI Tatuya's avatar
JINMEI Tatuya committed
712
713
    for (int i = 0 ; i < 4 ; ++i) {
        const unsigned char version_byte = get_byte(in);
714
715
716
717
718
719
        if (PROTOCOL_VERSION[i] != version_byte) {
            throw DecodeError("Protocol version incorrect");
        }
    }
    length -= 4;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
720
    return (decode_hash(in, length));
721
722
723
724
725
726
727
}

//
// Encode into wire format.
//

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
728
encode_length(const unsigned int length, unsigned char type) {
729
730
731
    std::stringstream ss;

    if (length <= 0x000000ff) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
732
        const unsigned char val = (length & 0x000000ff);
733
734
735
        type |= ITEM_LENGTH_8;
        ss << type << val;
    } else if (length <= 0x0000ffff) {
736
        unsigned char val[2];
737
738
739
        val[0] = (length & 0x0000ff00) >> 8;
        val[1] = (length & 0x000000ff);
        type |= ITEM_LENGTH_16;
740
        ss << type << val[0] << val[1];
741
    } else {
742
        unsigned char val[4];
743
744
745
746
747
        val[0] = (length & 0xff000000) >> 24;
        val[1] = (length & 0x00ff0000) >> 16;
        val[2] = (length & 0x0000ff00) >> 8;
        val[3] = (length & 0x000000ff);
        type |= ITEM_LENGTH_32;
748
        ss << type << val[0] << val[1] << val[2] << val[3];
749
750
751
752
753
    }
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
754
Element::toWire(const int omit_length) {
755
    std::stringstream ss;
Jelte Jansen's avatar
Jelte Jansen committed
756
757
758
    toWire(ss, omit_length);
    return ss.str();
}
759

Jelte Jansen's avatar
Jelte Jansen committed
760
void
761
762
763
StringElement::toWire(std::stringstream& ss,
                      const int omit_length UNUSED_PARAM)
{
764
    unsigned int length = stringValue().length();
765
    ss << encode_length(length, ITEM_UTF8) << stringValue();
766
767
}

Jelte Jansen's avatar
Jelte Jansen committed
768
void
769
770
771
IntElement::toWire(std::stringstream& ss,
                   const int omit_length UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
772
773
    const std::string& s = str();
    ss << encode_length(s.length(), ITEM_INT) << s;
774
775
}

Jelte Jansen's avatar
Jelte Jansen committed
776
void
777
778
779
BoolElement::toWire(std::stringstream& ss,
                    const int omit_length UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
780
    ss << encode_length(1, ITEM_BOOL);
781
    if (boolValue()) {
Jelte Jansen's avatar
Jelte Jansen committed
782
783
784
785
        ss << 0x01;
    } else {
        ss << 0x00;
    }
786
787
}

Jelte Jansen's avatar
Jelte Jansen committed
788
void
789
790
791
DoubleElement::toWire(std::stringstream& ss,
                      const int omit_length UNUSED_PARAM)
{
792
793
794
    std::stringstream text;

    text << str();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
795
    const int length = text.str().length();
796
    ss << encode_length(length, ITEM_REAL) << text.str();
797
798
}

Jelte Jansen's avatar
Jelte Jansen committed
799
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
800
ListElement::toWire(std::stringstream& ss, const int omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
801
    std::stringstream ss2;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
802
803
    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin() ;
804
         it != v.end() ; ++it) {
Jelte Jansen's avatar
Jelte Jansen committed
805
        (*it)->toWire(ss2, 0);
806
807
    }

808

809
    if (omit_length) {
810
        stringbuf *ss2_buf = ss2.rdbuf();
811
        ss2_buf->pubseekpos(0);
812
813
814
        if (ss2_buf->in_avail() > 0) {
            ss << ss2_buf;
        }
815
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
816
817
818
        stringbuf *ss2_buf = ss2.rdbuf();
        ss2_buf->pubseekpos(0);
        ss << encode_length(ss2_buf->in_avail(), ITEM_LIST);
819
820
821
        if (ss2_buf->in_avail() > 0) {
            ss << ss2_buf;
        }
822
823
824
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
825
namespace {
Jelte Jansen's avatar
Jelte Jansen committed
826
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
827
828
encode_tag(std::stringstream& ss, const std::string &s) {
    const unsigned char val = s.length() & 0x000000ff;
829
830
831

    ss << val << s;
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
832
}
833

Jelte Jansen's avatar
Jelte Jansen committed
834
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
835
MapElement::toWire(std::stringstream& ss, int omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
836
    std::stringstream ss2;
837
838
839
840
841

    //
    // If we don't want the length, we will want the protocol header
    //
    if (omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
842
843
        ss2 << PROTOCOL_VERSION[0] << PROTOCOL_VERSION[1];
        ss2 << PROTOCOL_VERSION[2] << PROTOCOL_VERSION[3];
844
845
    }

846
847
    const std::map<std::string, ElementPtr>& m = mapValue();
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin() ;
848
         it != m.end() ; ++it) {
Jelte Jansen's avatar
Jelte Jansen committed
849
850
        encode_tag(ss2, (*it).first);
        (*it).second->toWire(ss2, 0);
851
852
853
854
855
856
    }

    //
    // add length if needed
    //
    if (omit_length) {
857
        stringbuf *ss2_buf = ss2.rdbuf();
858
859
        ss2_buf->pubseekpos(0);
        if (ss2_buf->in_avail()) {
860
861
            ss << ss2_buf;
        }
862
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
863
864
865
        stringbuf *ss2_buf = ss2.rdbuf();
        ss2_buf->pubseekpos(0);
        ss << encode_length(ss2_buf->in_avail(), ITEM_HASH);
866
        if (ss2_buf->in_avail()) {
867
868
            ss << ss2_buf;
        }
869
870
871
872
873
874
    }
}

bool
MapElement::find(const std::string& id, ElementPtr& t) {
    try {
Jelte Jansen's avatar
Jelte Jansen committed
875
        ElementPtr p = find(id);
876
877
878
879
        if (p) {
            t = p;
            return true;
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
880
    } catch (const TypeError& e) {
881
882
883
884
        // ignore
    }
    return false;
}
885

886
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
887
IntElement::equals(ElementPtr other) {
888
889
890
891
892
    return (other->getType() == Element::integer) &&
           (i == other->intValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
893
DoubleElement::equals(ElementPtr other) {
894
895
896
897
898
    return (other->getType() == Element::real) &&
           (d == other->doubleValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
899
BoolElement::equals(ElementPtr other) {
900
901
902
903
904
    return (other->getType() == Element::boolean) &&
           (b == other->boolValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
905
StringElement::equals(ElementPtr other) {
906
907
908
909
910
    return (other->getType() == Element::string) &&
           (s == other->stringValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
911
ListElement::equals(ElementPtr other) {
912
    if (other->getType() == Element::list) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
913
        const int s = size();
914
915
916
        if (s != other->size()) {
            return false;
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
917
        for (int i = 0; i < s; ++i) {
918
919
920
921
922
923
924
925
926
927
928
            if (!get(i)->equals(other->get(i))) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
929
MapElement::equals(ElementPtr other) {
930
931
    if (other->getType() == Element::map) {
        std::map<std::string, ElementPtr> m = mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
932
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
             it != m.end() ; ++it) {
            if (other->contains((*it).first)) {
                if (!get((*it).first)->equals(other->get((*it).first))) {
                    return false;
                }
            } else {
                return false;
            }
        }
        // quickly walk through the other map too, to see if there's
        // anything in there that we don't have. We don't need to
        // compare those elements; if one of them is missing we
        // differ (and if it's not missing the loop above has checked
        // it)
        m = other->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
948
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
949
950
951
952
953
954
955
956
957
958
959
             it != m.end() ; ++it) {
            if (!contains((*it).first)) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

960
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
961
isNull(ElementPtr p) {
962
963
964
    return !p;
}

965
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
966
removeIdentical(ElementPtr a, const ElementPtr b) {
967
968
969
    if (!b) {
        return;
    }
970
    if (a->getType() != Element::map || b->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
971
        isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
972
    }
973

974
    std::map<std::string, ElementPtr> m = a->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
975
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
976
977
978
979
980
981
982
         it != m.end() ; ++it) {
        if (b->contains((*it).first)) {
            if (a->get((*it).first)->equals(b->get((*it).first))) {
                a->remove((*it).first);
            }
        }
    }
983
}
984

985
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
986
merge(ElementPtr element, const ElementPtr other) {
987
988
    if (element->getType() != Element::map ||
        other->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
989
        isc_throw(TypeError, "merge arguments not MapElements");
990
991
992
    }
    
    std::map<std::string, ElementPtr> m = other->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
993
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
994
995
996
997
998
999
1000
         it != m.end() ; ++it) {
        if ((*it).second) {
            element->set((*it).first, (*it).second);
        } else if (element->contains((*it).first)) {
            element->remove((*it).first);
        }
    }
1001
}
1002

JINMEI Tatuya's avatar
JINMEI Tatuya committed
1003
1004
}
}