1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #define __json_h__ 8 #include <vector> 9 #include "gtest/gtest.h" 10 #include "nss_scoped_ptrs.h" 11 #include "pk11pub.h" 12 13 // If we make a few assumptions about the file, parsing JSON can be easy. 14 // This is not a full parser, it only works on a narrow set of inputs. 15 class JsonReader { 16 public: JsonReader(const std::string & n)17 JsonReader(const std::string &n) : buf_(), available_(0), i_(0) { 18 f_.reset(PR_Open(n.c_str(), PR_RDONLY, 00600)); 19 EXPECT_TRUE(f_) << "error opening vectors from: " << n; 20 buf_[0] = 0; 21 } 22 next()23 void next() { i_++; } peek()24 uint8_t peek() { 25 TopUp(); 26 return buf_[i_]; 27 } take()28 uint8_t take() { 29 uint8_t v = peek(); 30 next(); 31 return v; 32 } 33 34 // No input checking, overflow protection, or any safety. 35 // Returns 0 if there isn't a number here rather than aborting. ReadInt()36 uint64_t ReadInt() { 37 SkipWhitespace(); 38 uint8_t c = peek(); 39 uint64_t v = 0; 40 while (c >= '0' && c <= '9') { 41 v = v * 10 + c - '0'; 42 next(); 43 c = peek(); 44 } 45 return v; 46 } 47 48 // No input checking, no unicode, no escaping (not even \"), just read ASCII. ReadString()49 std::string ReadString() { 50 SkipWhitespace(); 51 if (peek() != '"') { 52 return ""; 53 } 54 next(); 55 56 std::string s; 57 uint8_t c = take(); 58 while (c != '"') { 59 s.push_back(c); 60 c = take(); 61 } 62 return s; 63 } 64 ReadLabel()65 std::string ReadLabel() { 66 std::string s = ReadString(); 67 SkipWhitespace(); 68 EXPECT_EQ(take(), ':'); 69 return s; 70 } 71 ReadHex()72 std::vector<uint8_t> ReadHex() { 73 SkipWhitespace(); 74 uint8_t c = take(); 75 EXPECT_EQ(c, '"'); 76 std::vector<uint8_t> v; 77 c = take(); 78 while (c != '"') { 79 v.push_back(JsonReader::Hex(c) << 4 | JsonReader::Hex(take())); 80 c = take(); 81 } 82 return v; 83 } 84 85 bool NextItem(uint8_t h = '{', uint8_t t = '}') { 86 SkipWhitespace(); 87 switch (uint8_t c = take()) { 88 case ',': 89 return true; 90 case '{': 91 case '[': 92 EXPECT_EQ(c, h); 93 SkipWhitespace(); 94 if (peek() == t) { 95 next(); 96 return false; 97 } 98 return true; 99 case '}': 100 case ']': 101 EXPECT_EQ(c, t); 102 return false; 103 default: 104 ADD_FAILURE() << "Unexpected '" << c << "'"; 105 } 106 return false; 107 } 108 NextItemArray()109 bool NextItemArray() { return NextItem('[', ']'); } 110 SkipValue()111 void SkipValue() { 112 uint8_t c = take(); 113 if (c == '"') { 114 do { 115 c = take(); 116 } while (c != '"'); 117 } else if (c >= '0' && c <= '9') { 118 c = peek(); 119 while (c >= '0' && c <= '9') { 120 next(); 121 c = peek(); 122 } 123 } else { 124 ADD_FAILURE() << "No idea how to skip'" << c << "'"; 125 } 126 } 127 128 private: TopUp()129 void TopUp() { 130 if (available_ > i_) { 131 return; 132 } 133 i_ = 0; 134 if (!f_) { 135 return; 136 } 137 PRInt32 res = PR_Read(f_.get(), buf_, sizeof(buf_)); 138 if (res > 0) { 139 available_ = static_cast<size_t>(res); 140 } else { 141 available_ = 1; 142 f_.reset(nullptr); 143 buf_[0] = 0; 144 } 145 } 146 SkipWhitespace()147 void SkipWhitespace() { 148 uint8_t c = peek(); 149 while (c && (c == ' ' || c == '\t' || c == '\r' || c == '\n')) { 150 next(); 151 c = peek(); 152 } 153 } 154 155 // This only handles lowercase. Hex(uint8_t c)156 uint8_t Hex(uint8_t c) { 157 if (c >= '0' && c <= '9') { 158 return c - '0'; 159 } 160 EXPECT_TRUE(c >= 'a' && c <= 'f'); 161 return c - 'a' + 10; 162 } 163 164 ScopedPRFileDesc f_; 165 uint8_t buf_[4096]; 166 size_t available_; 167 size_t i_; 168 }; 169