data.cc 26.6 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
#include "config.h"

19
20
#include "data.h"

JINMEI Tatuya's avatar
JINMEI Tatuya committed
21
#include <cassert>
22
23
#include <cstdio>
#include <iostream>
24
#include <string>
25
26
#include <sstream>

27
#include <boost/algorithm/string.hpp> // for iequals
28
29
30

using namespace std;

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

34
const unsigned char ITEM_BLOB = 0x01;
35
36
37
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
38
const unsigned char ITEM_BOOL = 0x05;
39
const unsigned char ITEM_INT  = 0x06;
40
const unsigned char ITEM_REAL = 0x07;
41
const unsigned char ITEM_UTF8 = 0x08;
42
43
44
45
46
47
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;
48
}
49

JINMEI Tatuya's avatar
JINMEI Tatuya committed
50
51
52
namespace isc {
namespace data {

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
//
// The following methods are effectively empty, and their parameters are
// unused.  To silence compilers that warn unused function parameters,
// we specify a (compiler dependent) special keyword when available.
// It's defined in config.h, and to avoid including this header file from
// installed files we define the methods here.
//
bool
Element::getValue(int& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(double& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(bool& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(std::string& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(std::vector<ElementPtr>& t UNUSED_PARAM) {
    return false;
}

bool
Element::getValue(std::map<std::string, ElementPtr>& t UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const int v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const double v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const bool t UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const std::string& v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const std::vector<ElementPtr>& v UNUSED_PARAM) {
    return false;
}

bool
Element::setValue(const std::map<std::string, ElementPtr>& v UNUSED_PARAM)
{
    return false;
}

ElementPtr
Element::get(const int i UNUSED_PARAM) {
    isc_throw(TypeError, "get(int) called on a non-list Element");
}

void
Element::set(const size_t i UNUSED_PARAM, ElementPtr element UNUSED_PARAM) {
    isc_throw(TypeError, "set(int, element) called on a non-list Element");
}

void
Element::add(ElementPtr element UNUSED_PARAM) {
    isc_throw(TypeError, "add() called on a non-list Element");
}

void
Element::remove(const int i UNUSED_PARAM) {
    isc_throw(TypeError, "remove(int) called on a non-list Element");
}

size_t
Element::size() {
    isc_throw(TypeError, "size() called on a non-list Element");
}

ElementPtr
Element::get(const std::string& name UNUSED_PARAM) {
    isc_throw(TypeError, "get(string) called on a non-map Element");
}

void
Element::set(const std::string& name UNUSED_PARAM,
             ElementPtr element UNUSED_PARAM)
{
    isc_throw(TypeError, "set(name, element) called on a non-map Element");
}

void
Element::remove(const std::string& name UNUSED_PARAM) {
    isc_throw(TypeError, "remove(string) called on a non-map Element");
}

bool
Element::contains(const std::string& name UNUSED_PARAM) {
    isc_throw(TypeError, "contains(string) called on a non-map Element");
}

ElementPtr
Element::find(const std::string& identifier UNUSED_PARAM) {
    isc_throw(TypeError, "find(string) called on a non-map Element");
}

bool
Element::find(const std::string& identifier UNUSED_PARAM,
              ElementPtr& t UNUSED_PARAM)
{
    return false;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
180
181
namespace {
inline void
182
throwParseError(const std::string& error, const std::string& file, int line = 0, int pos = 0)
Jelte Jansen's avatar
Jelte Jansen committed
183
184
185
{
    if (line != 0 || pos != 0) {
        std::stringstream ss;
186
        ss << error << " in " + file + ":" << line << ":" << pos;
Jelte Jansen's avatar
Jelte Jansen committed
187
188
189
190
        throw ParseError(ss.str());
    } else {
        throw ParseError(error);
    }
191
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
192
}
193

Jelte Jansen's avatar
Jelte Jansen committed
194
195
std::ostream& operator <<(std::ostream &out, const isc::data::ElementPtr& e) {
    return out << e->str();
196
197
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
198
bool operator==(const isc::data::ElementPtr a, const isc::data::ElementPtr b) {
199
200
201
    return a->equals(b);
};

202
203
204
205
//
// factory functions
//
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
206
Element::create(const int i) {
207
208
209
210
211
212
213
214
    try {
        return ElementPtr(new IntElement(i));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
215
Element::create(const double d) {
216
217
218
219
220
221
222
223
    try {
        return ElementPtr(new DoubleElement(d));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
224
Element::create(const std::string& s) {
225
226
227
228
229
230
231
232
    try {
        return ElementPtr(new StringElement(s));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
233
Element::create(const bool b) {
234
235
236
237
238
239
240
241
    try {
        return ElementPtr(new BoolElement(b));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
242
Element::create(const std::vector<ElementPtr>& v) {
243
244
245
246
247
248
249
250
    try {
        return ElementPtr(new ListElement(v));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
251
Element::create(const std::map<std::string, ElementPtr>& m) {
252
253
254
255
256
257
258
259
260
    try {
        return ElementPtr(new MapElement(m));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}


//
261
// helper functions for createFromString factory
262
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
263
264
namespace {
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
265
266
char_in(const char c, const char *chars) {
    for (size_t i = 0; i < strlen(chars); ++i) {
267
268
269
270
271
272
273
        if (chars[i] == c) {
            return true;
        }
    }
    return false;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
274
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
275
skip_chars(std::istream &in, const char *chars, int& line, int& pos) {
276
277
    char c = in.peek();
    while (char_in(c, chars) && c != EOF) {
278
        if (c == '\n') {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
279
            ++line;
280
281
            pos = 1;
        } else {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
282
            ++pos;
283
        }
284
285
286
287
288
289
290
        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
291
// unless that character is specified in the optional may_skip
292
293
//
// the character found is left on the stream
JINMEI Tatuya's avatar
JINMEI Tatuya committed
294
void
Jelte Jansen's avatar
Jelte Jansen committed
295
296
skip_to(std::istream &in, const std::string& file, int& line,
        int& pos, const char* chars, const char* may_skip="")
297
298
{
    char c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
299
    ++pos;
300
    while (c != EOF) {
301
302
        if (c == '\n') {
            pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
303
            ++line;
304
        }
305
306
        if (char_in(c, may_skip)) {
            c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
307
            ++pos;
308
309
        } else if (char_in(c, chars)) {
            while(char_in(in.peek(), may_skip)) {
310
311
                if (in.peek() == '\n') {
                    pos = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
312
                    ++line;
313
                }
314
                in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
315
                ++pos;
316
317
            }
            in.putback(c);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
318
            --pos;
319
            return;
320
        } else {
Jelte Jansen's avatar
Jelte Jansen committed
321
            throwParseError(std::string("'") + c + "' read, one of \"" + chars + "\" expected", file, line, pos);
322
323
        }
    }
Jelte Jansen's avatar
Jelte Jansen committed
324
    throwParseError(std::string("EOF read, one of \"") + chars + "\" expected", file, line, pos);
325
326
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
327
std::string
328
329
str_from_stringstream(std::istream &in, const std::string& file, const int line,
                      int& pos) throw (ParseError)
330
331
332
333
{
    char c = 0;
    std::stringstream ss;
    c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
334
    ++pos;
335
336
    if (c == '"') {
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
337
        ++pos;
338
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
339
        throwParseError("String expected", file, line, pos);
340
341
342
343
344
    }
    while (c != EOF && c != '"') {
        ss << c;
        if (c == '\\' && in.peek() == '"') {
            ss << in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
345
            ++pos;
346
347
        }
        c = in.get();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
348
        ++pos;
349
350
351
352
    }
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
353
std::string
354
word_from_stringstream(std::istream &in, int& pos) {
355
356
357
358
    std::stringstream ss;
    while (isalpha(in.peek())) {
        ss << (char) in.get();
    }
359
    pos += ss.str().size();
360
361
362
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
363
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
364
count_chars_i(int i) {
365
366
    int result = 1;
    while (i > 10) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
367
368
        ++result;
        i = i / 10;
369
370
371
372
    }
    return result;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
373
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
374
count_chars_d(double d) {
375
    int result = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
376
377
    while (d < 1.0) {
        ++result;
378
379
380
381
        d = d * 10;
    }
    return result;
}
382

JINMEI Tatuya's avatar
JINMEI Tatuya committed
383
ElementPtr
384
from_stringstream_int_or_double(std::istream &in, int &pos) {
385
386
    int i;
    in >> i;
387
    pos += count_chars_i(i);
388
389
390
    if (in.peek() == '.') {
        double d;
        in >> d;
391
        pos += count_chars_d(i);
392
393
394
395
396
397
398
        d += i;
        return Element::create(d);
    } else {
        return Element::create(i);
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
399
ElementPtr
400
401
from_stringstream_bool(std::istream &in, const std::string& file,
                       const int line, int& pos)
402
{
403
    const std::string word = word_from_stringstream(in, pos);
404
405
406
407
408
    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
409
410
411
        throwParseError(std::string("Bad boolean value: ") + word, file, line, pos);
        // above is a throw shortcur, return empty is never reached
        return ElementPtr();
412
413
414
    }
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
415
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
416
from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
417
{
Jelte Jansen's avatar
Jelte Jansen committed
418
    return Element::create(str_from_stringstream(in, file, line, pos));
419
420
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
421
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
422
from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
423
424
425
426
427
{
    char c = 0;
    std::vector<ElementPtr> v;
    ElementPtr cur_list_element;

428
    skip_chars(in, " \t\n", line, pos);
429
    while (c != EOF && c != ']') {
430
        if (in.peek() != ']') {
Jelte Jansen's avatar
Jelte Jansen committed
431
            cur_list_element = Element::createFromString(in, file, line, pos);
432
            v.push_back(cur_list_element);
Jelte Jansen's avatar
Jelte Jansen committed
433
            skip_to(in, file, line, pos, ",]", " \t\n");
434
435
        }
        c = in.get();
436
        pos++;
437
438
439
440
    }
    return Element::create(v);
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
441
ElementPtr
442
443
from_stringstream_map(std::istream &in, const std::string& file, int& line,
                      int& pos)
444
445
{
    std::map<std::string, ElementPtr> m;
446
    skip_chars(in, " \t\n", line, pos);
447
    char c = in.peek();
448
449
    if (c == '}') {
        // empty map, skip closing curly
450
        c = in.get();
451
452
    } else {
        while (c != EOF && c != '}') {
453
454
            std::pair<std::string, ElementPtr> p;

455
            p.first = str_from_stringstream(in, file, line, pos);
456
457
458
459
460
461
            if (p.first.length() > 255) {
                // Map tag has one-byte length field in wire format, so the
                // length cannot exceed 255.
                throwParseError("Map tag is too long", file, line, pos);
            }

462
463
464
465
466
467
468
469
470
471
            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++;
        }
472
473
474
    }
    return Element::create(m);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
475
}
476
477

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
478
Element::createFromString(std::istream& in) throw(ParseError) {
479
    int line = 1, pos = 1;
480
481
482
483
    return createFromString(in, "<istream>", line, pos);
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
484
Element::createFromString(std::istream& in, const std::string& file_name) throw(ParseError)
485
486
487
{
    int line = 1, pos = 1;
    return createFromString(in, file_name, line, pos);
488
489
490
}

ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
491
Element::createFromString(std::istream &in, const std::string& file, int& line, int& pos) throw(ParseError)
492
493
494
495
{
    char c = 0;
    ElementPtr element;
    bool el_read = false;
496
    skip_chars(in, " \n\t", line, pos);
497
498
    while (c != EOF && !el_read) {
        c = in.get();
499
        pos++;
500
501
502
503
504
505
506
507
508
509
510
511
        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);
512
                element = from_stringstream_int_or_double(in, pos);
513
514
515
516
517
518
519
                el_read = true;
                break;
            case 't':
            case 'T':
            case 'f':
            case 'F':
                in.putback(c);
Jelte Jansen's avatar
Jelte Jansen committed
520
                element = from_stringstream_bool(in, file, line, pos);
521
522
523
524
                el_read = true;
                break;
            case '"':
                in.putback('"');
Jelte Jansen's avatar
Jelte Jansen committed
525
                element = from_stringstream_string(in, file, line, pos);
526
527
528
                el_read = true;
                break;
            case '[':
Jelte Jansen's avatar
Jelte Jansen committed
529
                element = from_stringstream_list(in, file, line, pos);
530
531
532
                el_read = true;
                break;
            case '{':
Jelte Jansen's avatar
Jelte Jansen committed
533
                element = from_stringstream_map(in, file, line, pos);
534
535
                el_read = true;
                break;
536
537
            case EOF:
                break;
538
            default:
Jelte Jansen's avatar
Jelte Jansen committed
539
                throwParseError(std::string("error: unexpected character ") + c, file, line, pos);
540
541
542
543
544
545
                break;
        }
    }
    if (el_read) {
        return element;
    } else {
546
        throw ParseError("nothing read");
547
548
549
    }
}

550
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
551
Element::createFromString(const std::string &in) {
552
553
    std::stringstream ss;
    ss << in;
554
    return createFromString(ss, "<string>");
555
}
556

557
558
559
560
//
// a general to_str() function
//
std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
561
IntElement::str() {
562
    std::stringstream ss;
563
    ss << intValue();
564
565
566
567
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
568
DoubleElement::str() {
569
    std::stringstream ss;
570
    ss << doubleValue();
571
572
573
574
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
575
BoolElement::str() {
576
577
578
579
580
581
582
583
    if (b) {
        return "True";
    } else {
        return "False";
    }
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
584
StringElement::str() {
585
586
    std::stringstream ss;
    ss << "\"";
587
    ss << stringValue();
588
589
590
591
592
    ss << "\"";
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
593
ListElement::str() {
594
595
    std::stringstream ss;
    ss << "[ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
596
597
598
599

    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin();
         it != v.end(); ++it) {
600
601
602
603
604
605
606
607
608
609
        if (it != v.begin()) {
            ss << ", ";
        }
        ss << (*it)->str();
    }
    ss << " ]";
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
610
MapElement::str() {
611
612
    std::stringstream ss;
    ss << "{";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
613
614
615
616

    const std::map<std::string, ElementPtr>& m = mapValue();
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
         it != m.end(); ++it) {
617
618
619
620
        if (it != m.begin()) {
            ss << ", ";
        }
        ss << "\"" << (*it).first << "\": ";
621
622
623
624
625
        if ((*it).second) {
            ss << (*it).second->str();
        } else {
            ss << "None";
        }
626
627
628
629
630
    }
    ss << "}";
    return ss.str();
}

631
// throws when one of the types in the path (except the one
632
633
634
635
// 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
636
637
MapElement::find(const std::string& id) {
    const size_t sep = id.find('/');
638
639
640
641
642
    if (sep == std::string::npos) {
        return get(id);
    } else {
        ElementPtr ce = get(id.substr(0, sep));
        if (ce) {
643
            // ignore trailing slash
JINMEI Tatuya's avatar
JINMEI Tatuya committed
644
645
            if  (sep + 1 != id.size()) {
                return ce->find(id.substr(sep + 1));
646
647
648
            } else {
                return ce;
            }
649
650
651
652
653
654
655
656
657
        } else {
            return ElementPtr();
        }
    }
}

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
661
unsigned char
JINMEI Tatuya's avatar
JINMEI Tatuya committed
662
663
get_byte(std::stringstream& in) {
    const int c = in.get();
664
665
666
667
668
669
670
671
    if (c == EOF) {
        throw DecodeError("End of data while decoding wire format message");
    }

    return c;
}

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
675
    const int len = get_byte(in);
676
677
678
679
680
681
682
683
684
685
686
687
    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
688
ElementPtr
689
decode_bool(std::stringstream& in) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
690
    const char c = in.get();
691
692
    
    if (c == '1') {
Jelte Jansen's avatar
Jelte Jansen committed
693
694
695
696
697
698
        return Element::create(true);
    } else {
        return Element::create(false);
    }
}

699
ElementPtr
700
701
702
decode_int(std::stringstream& in) {
    int me;
    return from_stringstream_int_or_double(in, me);
703
704
}

705
ElementPtr
706
707
708
decode_real(std::stringstream& in) {
    int me;
    return from_stringstream_int_or_double(in, me);
709
710
}

711
ElementPtr
712
713
decode_blob(std::stringstream& in, const int item_length) {
    vector<char> buf(item_length + 1);
714

715
    in.read(&buf[0], item_length);
716
717
718
719
720
    if (in.fail()) {
        throw DecodeError();
    }
    buf[item_length] = 0;

721
    return Element::create(std::string(&buf[0], item_length));
722
723
724
}

ElementPtr
725
decode_hash(std::stringstream& in, int item_length) {
726
727
728
729
730
731
732
733
734
735
736
737
738
    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
739
decode_list(std::stringstream& in, int item_length) {
740
741
742
743
744
745
746
747
748
    std::vector<ElementPtr> v;

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

ElementPtr
749
decode_null() {
750
751
752
753
    return Element::create("NULL");
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
754
decode_element(std::stringstream& in, int& in_length) {
755
756
    ElementPtr element;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
757
758
759
    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;
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
    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
782
    case ITEM_BOOL:
783
        element = decode_bool(in);
Jelte Jansen's avatar
Jelte Jansen committed
784
        break;
785
    case ITEM_INT:
786
        element = decode_int(in);
787
        break;
788
    case ITEM_REAL:
789
        element = decode_real(in);
790
        break;
791
792
793
794
    case ITEM_BLOB:
        element = decode_blob(in, item_length);
        break;
    case ITEM_UTF8:
795
796
        // XXXMLG currently identical to decode_blob
        element = decode_blob(in, item_length);
797
798
799
800
801
802
803
804
        break;
    case ITEM_HASH:
        element = decode_hash(in, item_length);
        break;
    case ITEM_LIST:
        element = decode_list(in, item_length);
        break;
    case ITEM_NULL:
805
        element = decode_null();
806
807
808
809
810
        break;
    }

    return (element);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
811
}
812
813

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
814
Element::fromWire(const std::string& s) {
815
816
    std::stringstream ss;
    ss << s;
817
    return fromWire(ss, s.length());
818
819
820
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
821
Element::fromWire(std::stringstream& in, int length) {
822
823
824
    //
    // Check protocol version
    //
JINMEI Tatuya's avatar
JINMEI Tatuya committed
825
826
    for (int i = 0 ; i < 4 ; ++i) {
        const unsigned char version_byte = get_byte(in);
827
828
829
830
831
832
        if (PROTOCOL_VERSION[i] != version_byte) {
            throw DecodeError("Protocol version incorrect");
        }
    }
    length -= 4;

JINMEI Tatuya's avatar
JINMEI Tatuya committed
833
    return (decode_hash(in, length));
834
835
836
837
838
839
840
}

//
// Encode into wire format.
//

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
841
encode_length(const unsigned int length, unsigned char type) {
842
843
844
    std::stringstream ss;

    if (length <= 0x000000ff) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
845
        const unsigned char val = (length & 0x000000ff);
846
847
848
        type |= ITEM_LENGTH_8;
        ss << type << val;
    } else if (length <= 0x0000ffff) {
849
        unsigned char val[2];
850
851
852
        val[0] = (length & 0x0000ff00) >> 8;
        val[1] = (length & 0x000000ff);
        type |= ITEM_LENGTH_16;
853
        ss << type << val[0] << val[1];
854
    } else {
855
        unsigned char val[4];
856
857
858
859
860
        val[0] = (length & 0xff000000) >> 24;
        val[1] = (length & 0x00ff0000) >> 16;
        val[2] = (length & 0x0000ff00) >> 8;
        val[3] = (length & 0x000000ff);
        type |= ITEM_LENGTH_32;
861
        ss << type << val[0] << val[1] << val[2] << val[3];
862
863
864
865
866
    }
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
867
Element::toWire(const int omit_length) {
868
    std::stringstream ss;
Jelte Jansen's avatar
Jelte Jansen committed
869
870
871
    toWire(ss, omit_length);
    return ss.str();
}
872

Jelte Jansen's avatar
Jelte Jansen committed
873
void
874
875
876
StringElement::toWire(std::stringstream& ss,
                      const int omit_length UNUSED_PARAM)
{
877
    unsigned int length = stringValue().length();
878
    ss << encode_length(length, ITEM_UTF8) << stringValue();
879
880
}

Jelte Jansen's avatar
Jelte Jansen committed
881
void
882
883
884
IntElement::toWire(std::stringstream& ss,
                   const int omit_length UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
885
886
    const std::string& s = str();
    ss << encode_length(s.length(), ITEM_INT) << s;
887
888
}

Jelte Jansen's avatar
Jelte Jansen committed
889
void
890
891
892
BoolElement::toWire(std::stringstream& ss,
                    const int omit_length UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
893
    ss << encode_length(1, ITEM_BOOL);
894
    if (boolValue()) {
Jelte Jansen's avatar
Jelte Jansen committed
895
896
897
898
        ss << 0x01;
    } else {
        ss << 0x00;
    }
899
900
}

Jelte Jansen's avatar
Jelte Jansen committed
901
void
902
903
904
DoubleElement::toWire(std::stringstream& ss,
                      const int omit_length UNUSED_PARAM)
{
905
906
907
    std::stringstream text;

    text << str();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
908
    const int length = text.str().length();
909
    ss << encode_length(length, ITEM_REAL) << text.str();
910
911
}

Jelte Jansen's avatar
Jelte Jansen committed
912
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
913
ListElement::toWire(std::stringstream& ss, const int omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
914
    std::stringstream ss2;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
915
916
    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin() ;
917
         it != v.end() ; ++it) {
Jelte Jansen's avatar
Jelte Jansen committed
918
        (*it)->toWire(ss2, 0);
919
920
    }

921

922
    if (omit_length) {
923
        stringbuf *ss2_buf = ss2.rdbuf();
924
        ss2_buf->pubseekpos(0);
925
926
927
        if (ss2_buf->in_avail() > 0) {
            ss << ss2_buf;
        }
928
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
929
930
931
        stringbuf *ss2_buf = ss2.rdbuf();
        ss2_buf->pubseekpos(0);
        ss << encode_length(ss2_buf->in_avail(), ITEM_LIST);
932
933
934
        if (ss2_buf->in_avail() > 0) {
            ss << ss2_buf;
        }
935
936
937
    }
}

Jelte Jansen's avatar
Jelte Jansen committed
938
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
939
MapElement::toWire(std::stringstream& ss, int omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
940
    std::stringstream ss2;
941
942
943
944
945

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

950
    const std::map<std::string, ElementPtr>& m = mapValue();
951
952
953
954
955
956
957
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
         it != m.end(); ++it) {
        const size_t taglen = (*it).first.length();
        assert(taglen <= 0xff);
        const unsigned char val = (taglen & 0x000000ff);
        ss2 << val << (*it).first;

Jelte Jansen's avatar
Jelte Jansen committed
958
        (*it).second->toWire(ss2, 0);
959
960
961
962
963
964
    }

    //
    // add length if needed
    //
    if (omit_length) {
965
        stringbuf *ss2_buf = ss2.rdbuf();
966
967
        ss2_buf->pubseekpos(0);
        if (ss2_buf->in_avail()) {
968
969
            ss << ss2_buf;
        }
970
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
971
972
973
        stringbuf *ss2_buf = ss2.rdbuf();
        ss2_buf->pubseekpos(0);
        ss << encode_length(ss2_buf->in_avail(), ITEM_HASH);
974
        if (ss2_buf->in_avail()) {
975
976
            ss << ss2_buf;
        }
977
978
979
980
981
982
    }
}

bool
MapElement::find(const std::string& id, ElementPtr& t) {
    try {
Jelte Jansen's avatar
Jelte Jansen committed
983
        ElementPtr p = find(id);
984
985
986
987
        if (p) {
            t = p;
            return true;
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
988
    } catch (const TypeError& e) {
989
990
991
992
        // ignore
    }
    return false;
}
993

994
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
995
IntElement::equals(ElementPtr other) {
996
997
998
999
1000
    return (other->getType() == Element::integer) &&
           (i == other->intValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1001
DoubleElement::equals(ElementPtr other) {
1002
1003
1004
1005
1006
    return (other->getType() == Element::real) &&
           (d == other->doubleValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1007
BoolElement::equals(ElementPtr other) {
1008
1009
1010
1011
1012
    return (other->getType() == Element::boolean) &&
           (b == other->boolValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1013
StringElement::equals(ElementPtr other) {
1014
1015
1016
1017
1018
    return (other->getType() == Element::string) &&
           (s == other->stringValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1019
ListElement::equals(ElementPtr other) {
1020
    if (other->getType() == Element::list) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1021
        const int s = size();
1022
1023
1024
        if (s != other->size()) {
            return false;
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1025
        for (int i = 0; i < s; ++i) {
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
            if (!get(i)->equals(other->get(i))) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1037
MapElement::equals(ElementPtr other) {
1038
1039
    if (other->getType() == Element::map) {
        std::map<std::string, ElementPtr> m = mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1040
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
             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
1056
        for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
             it != m.end() ; ++it) {
            if (!contains((*it).first)) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

1068
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1069
isNull(ElementPtr p) {
1070
1071
1072
    return !p;
}

1073
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1074
removeIdentical(ElementPtr a, const ElementPtr b) {
1075
1076
1077
    if (!b) {
        return;
    }
1078
    if (a->getType() != Element::map || b->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1079
        isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1080
    }
1081

1082
    std::map<std::string, ElementPtr> m = a->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1083
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
1084
1085
1086
1087
1088
1089
1090
         it != m.end() ; ++it) {
        if (b->contains((*it).first)) {
            if (a->get((*it).first)->equals(b->get((*it).first))) {
                a->remove((*it).first);
            }
        }
    }
1091
}
1092

1093
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1094
merge(ElementPtr element, const ElementPtr other) {
1095
1096
    if (element->getType() != Element::map ||
        other->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1097
        isc_throw(TypeError, "merge arguments not MapElements");
1098
1099
1100
    }
    
    std::map<std::string, ElementPtr> m = other->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1101
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
1102
1103
1104
1105
1106
1107
1108
         it != m.end() ; ++it) {
        if ((*it).second) {
            element->set((*it).first, (*it).second);
        } else if (element->contains((*it).first)) {
            element->remove((*it).first);
        }
    }
1109
}
1110

JINMEI Tatuya's avatar
JINMEI Tatuya committed
1111
1112
}
}