data.cc 26.8 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
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
         it != m.end(); ++it) {
        if ((*it).first.length() > 255) {
            isc_throw(TypeError, "Map tag is too long");
        }
    }
258
259
260
261
262
263
264
265
266
    try {
        return ElementPtr(new MapElement(m));
    } catch (std::bad_alloc) {
        return ElementPtr();
    }
}


//
267
// helper functions for createFromString factory
268
//
JINMEI Tatuya's avatar
JINMEI Tatuya committed
269
270
namespace {
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
271
272
char_in(const char c, const char *chars) {
    for (size_t i = 0; i < strlen(chars); ++i) {
273
274
275
276
277
278
279
        if (chars[i] == c) {
            return true;
        }
    }
    return false;
}

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

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
359
std::string
360
word_from_stringstream(std::istream &in, int& pos) {
361
362
363
364
    std::stringstream ss;
    while (isalpha(in.peek())) {
        ss << (char) in.get();
    }
365
    pos += ss.str().size();
366
367
368
    return ss.str();
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
369
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
370
count_chars_i(int i) {
371
372
    int result = 1;
    while (i > 10) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
373
374
        ++result;
        i = i / 10;
375
376
377
378
    }
    return result;
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
379
inline int
JINMEI Tatuya's avatar
JINMEI Tatuya committed
380
count_chars_d(double d) {
381
    int result = 1;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
382
383
    while (d < 1.0) {
        ++result;
384
385
386
387
        d = d * 10;
    }
    return result;
}
388

JINMEI Tatuya's avatar
JINMEI Tatuya committed
389
ElementPtr
390
from_stringstream_int_or_double(std::istream &in, int &pos) {
391
392
    int i;
    in >> i;
393
    pos += count_chars_i(i);
394
395
396
    if (in.peek() == '.') {
        double d;
        in >> d;
397
        pos += count_chars_d(i);
398
399
400
401
402
403
404
        d += i;
        return Element::create(d);
    } else {
        return Element::create(i);
    }
}

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
421
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
422
from_stringstream_string(std::istream& in, const std::string& file, int& line, int& pos)
423
{
Jelte Jansen's avatar
Jelte Jansen committed
424
    return Element::create(str_from_stringstream(in, file, line, pos));
425
426
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
427
ElementPtr
Jelte Jansen's avatar
Jelte Jansen committed
428
from_stringstream_list(std::istream &in, const std::string& file, int& line, int& pos)
429
430
431
432
433
{
    char c = 0;
    std::vector<ElementPtr> v;
    ElementPtr cur_list_element;

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

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

461
            p.first = str_from_stringstream(in, file, line, pos);
462
463
464
465
466
467
            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);
            }

468
469
470
471
472
473
474
475
476
477
            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++;
        }
478
479
480
    }
    return Element::create(m);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
481
}
482
483

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

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
490
Element::createFromString(std::istream& in, const std::string& file_name) throw(ParseError)
491
492
493
{
    int line = 1, pos = 1;
    return createFromString(in, file_name, line, pos);
494
495
496
}

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

556
ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
557
Element::createFromString(const std::string &in) {
558
559
    std::stringstream ss;
    ss << in;
560
    return createFromString(ss, "<string>");
561
}
562

563
564
565
566
//
// a general to_str() function
//
std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
567
IntElement::str() {
568
    std::stringstream ss;
569
    ss << intValue();
570
571
572
573
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
574
DoubleElement::str() {
575
    std::stringstream ss;
576
    ss << doubleValue();
577
578
579
580
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
581
BoolElement::str() {
582
583
584
585
586
587
588
589
    if (b) {
        return "True";
    } else {
        return "False";
    }
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
590
StringElement::str() {
591
592
    std::stringstream ss;
    ss << "\"";
593
    ss << stringValue();
594
595
596
597
598
    ss << "\"";
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
599
ListElement::str() {
600
601
    std::stringstream ss;
    ss << "[ ";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
602
603
604
605

    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin();
         it != v.end(); ++it) {
606
607
608
609
610
611
612
613
614
615
        if (it != v.begin()) {
            ss << ", ";
        }
        ss << (*it)->str();
    }
    ss << " ]";
    return ss.str();
}

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
616
MapElement::str() {
617
618
    std::stringstream ss;
    ss << "{";
JINMEI Tatuya's avatar
JINMEI Tatuya committed
619
620
621
622

    const std::map<std::string, ElementPtr>& m = mapValue();
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
         it != m.end(); ++it) {
623
624
625
626
        if (it != m.begin()) {
            ss << ", ";
        }
        ss << "\"" << (*it).first << "\": ";
627
628
629
630
631
        if ((*it).second) {
            ss << (*it).second->str();
        } else {
            ss << "None";
        }
632
633
634
635
636
    }
    ss << "}";
    return ss.str();
}

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

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
667
unsigned char
JINMEI Tatuya's avatar
JINMEI Tatuya committed
668
669
get_byte(std::stringstream& in) {
    const int c = in.get();
670
671
672
673
674
675
676
677
    if (c == EOF) {
        throw DecodeError("End of data while decoding wire format message");
    }

    return c;
}

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

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

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

711
ElementPtr
712
713
714
decode_real(std::stringstream& in) {
    int me;
    return from_stringstream_int_or_double(in, me);
715
716
}

717
ElementPtr
718
719
decode_blob(std::stringstream& in, const int item_length) {
    vector<char> buf(item_length + 1);
720

721
    in.read(&buf[0], item_length);
722
723
724
725
726
    if (in.fail()) {
        throw DecodeError();
    }
    buf[item_length] = 0;

727
    return Element::create(std::string(&buf[0], item_length));
728
729
730
}

ElementPtr
731
decode_hash(std::stringstream& in, int item_length) {
732
733
734
735
736
737
738
739
740
741
742
743
744
    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
745
decode_list(std::stringstream& in, int item_length) {
746
747
748
749
750
751
752
753
754
    std::vector<ElementPtr> v;

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

ElementPtr
755
decode_null() {
756
757
758
759
    return Element::create("NULL");
}

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
760
decode_element(std::stringstream& in, int& in_length) {
761
762
    ElementPtr element;

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

    return (element);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
817
}
818
819

ElementPtr
JINMEI Tatuya's avatar
JINMEI Tatuya committed
820
Element::fromWire(const std::string& s) {
821
822
    std::stringstream ss;
    ss << s;
823
    return fromWire(ss, s.length());
824
825
826
}

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
839
    return (decode_hash(in, length));
840
841
842
843
844
845
846
}

//
// Encode into wire format.
//

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
847
encode_length(const unsigned int length, unsigned char type) {
848
849
850
    std::stringstream ss;

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

std::string
JINMEI Tatuya's avatar
JINMEI Tatuya committed
873
Element::toWire(const int omit_length) {
874
    std::stringstream ss;
Jelte Jansen's avatar
Jelte Jansen committed
875
876
877
    toWire(ss, omit_length);
    return ss.str();
}
878

Jelte Jansen's avatar
Jelte Jansen committed
879
void
880
881
882
StringElement::toWire(std::stringstream& ss,
                      const int omit_length UNUSED_PARAM)
{
883
    unsigned int length = stringValue().length();
884
    ss << encode_length(length, ITEM_UTF8) << stringValue();
885
886
}

Jelte Jansen's avatar
Jelte Jansen committed
887
void
888
889
890
IntElement::toWire(std::stringstream& ss,
                   const int omit_length UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
891
892
    const std::string& s = str();
    ss << encode_length(s.length(), ITEM_INT) << s;
893
894
}

Jelte Jansen's avatar
Jelte Jansen committed
895
void
896
897
898
BoolElement::toWire(std::stringstream& ss,
                    const int omit_length UNUSED_PARAM)
{
JINMEI Tatuya's avatar
JINMEI Tatuya committed
899
    ss << encode_length(1, ITEM_BOOL);
900
    if (boolValue()) {
Jelte Jansen's avatar
Jelte Jansen committed
901
902
903
904
        ss << 0x01;
    } else {
        ss << 0x00;
    }
905
906
}

Jelte Jansen's avatar
Jelte Jansen committed
907
void
908
909
910
DoubleElement::toWire(std::stringstream& ss,
                      const int omit_length UNUSED_PARAM)
{
911
912
913
    std::stringstream text;

    text << str();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
914
    const int length = text.str().length();
915
    ss << encode_length(length, ITEM_REAL) << text.str();
916
917
}

Jelte Jansen's avatar
Jelte Jansen committed
918
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
919
ListElement::toWire(std::stringstream& ss, const int omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
920
    std::stringstream ss2;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
921
922
    const std::vector<ElementPtr>& v = listValue();
    for (std::vector<ElementPtr>::const_iterator it = v.begin() ;
923
         it != v.end() ; ++it) {
Jelte Jansen's avatar
Jelte Jansen committed
924
        (*it)->toWire(ss2, 0);
925
926
    }

927

928
    if (omit_length) {
929
        stringbuf *ss2_buf = ss2.rdbuf();
930
        ss2_buf->pubseekpos(0);
931
932
933
        if (ss2_buf->in_avail() > 0) {
            ss << ss2_buf;
        }
934
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
935
936
937
        stringbuf *ss2_buf = ss2.rdbuf();
        ss2_buf->pubseekpos(0);
        ss << encode_length(ss2_buf->in_avail(), ITEM_LIST);
938
939
940
        if (ss2_buf->in_avail() > 0) {
            ss << ss2_buf;
        }
941
942
943
    }
}

Jelte Jansen's avatar
Jelte Jansen committed
944
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
945
MapElement::toWire(std::stringstream& ss, int omit_length) {
Jelte Jansen's avatar
Jelte Jansen committed
946
    std::stringstream ss2;
947
948
949
950
951

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

956
    const std::map<std::string, ElementPtr>& m = mapValue();
957
958
959
960
961
962
963
    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
964
        (*it).second->toWire(ss2, 0);
965
966
967
968
969
970
    }

    //
    // add length if needed
    //
    if (omit_length) {
971
        stringbuf *ss2_buf = ss2.rdbuf();
972
973
        ss2_buf->pubseekpos(0);
        if (ss2_buf->in_avail()) {
974
975
            ss << ss2_buf;
        }
976
    } else {
Jelte Jansen's avatar
Jelte Jansen committed
977
978
979
        stringbuf *ss2_buf = ss2.rdbuf();
        ss2_buf->pubseekpos(0);
        ss << encode_length(ss2_buf->in_avail(), ITEM_HASH);
980
        if (ss2_buf->in_avail()) {
981
982
            ss << ss2_buf;
        }
983
984
985
986
987
988
    }
}

bool
MapElement::find(const std::string& id, ElementPtr& t) {
    try {
Jelte Jansen's avatar
Jelte Jansen committed
989
        ElementPtr p = find(id);
990
991
992
993
        if (p) {
            t = p;
            return true;
        }
JINMEI Tatuya's avatar
JINMEI Tatuya committed
994
    } catch (const TypeError& e) {
995
996
997
998
        // ignore
    }
    return false;
}
999

1000
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1001
IntElement::equals(ElementPtr other) {
1002
1003
1004
1005
1006
    return (other->getType() == Element::integer) &&
           (i == other->intValue());
}

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1007
DoubleElement::equals(ElementPtr other) {
1008
1009
1010
1011
1012
    return (other->getType() == Element::real) &&
           (d == other->doubleValue());
}

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

bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1019
StringElement::equals(ElementPtr other) {
1020
1021
1022
1023
1024
    return (other->getType() == Element::string) &&
           (s == other->stringValue());
}

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

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

1074
bool
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1075
isNull(ElementPtr p) {
1076
1077
1078
    return !p;
}

1079
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1080
removeIdentical(ElementPtr a, const ElementPtr b) {
1081
1082
1083
    if (!b) {
        return;
    }
1084
    if (a->getType() != Element::map || b->getType() != Element::map) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1085
        isc_throw(TypeError, "Non-map Elements passed to removeIdentical");
1086
    }
1087

1088
    std::map<std::string, ElementPtr> m = a->mapValue();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
1089
    for (std::map<std::string, ElementPtr>::const_iterator it = m.begin();
1090
1091
1092
1093
1094
1095
1096
         it != m.end() ; ++it) {
        if (b->contains((*it).first)) {
            if (a->get((*it).first)->equals(b->get((*it).first))) {
                a->remove((*it).first);
            }
        }
    }
1097
}
1098

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
1117
1118
}
}