OverviewFilesCommitsIssuesDocsReleasesPeers

hyperswarm-peer / liboverlay / test / json_mini.h

117 lines · c ref:
#ifndef HYPEROVERLAY_TEST_JSON_MINI_H
#define HYPEROVERLAY_TEST_JSON_MINI_H

// Minimal JSON reader for the conformance fixtures — only what those files use.
//
// It lives in test/, NOT in include/overlay/: liboverlay is dependency-free so it
// can be lifted out by moving the directory, and a JSON parser is a test
// convenience, not part of the library. Shared by test_messages.cpp (vectors.json)
// and test_protocol.cpp (live-transcripts.json) so the two cannot drift into
// different readings of the same fixtures.

#include <cstdlib>
#include <cstring>
#include <fstream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

struct Json {
	enum Type { NUL, BOOL, NUM, STR, ARR, OBJ } type = NUL;
	bool b = false;
	double num = 0;
	std::string str;
	std::vector<Json> arr;
	std::map<std::string, Json> obj;

	const Json *find(const std::string &k) const {
		auto it = obj.find(k);
		return it == obj.end() ? nullptr : &it->second;
	}
};

struct JsonParser {
	const char *p;
	void ws() { while (*p == ' ' || *p == '\n' || *p == '\t' || *p == '\r') p++; }
	Json parse() {
		ws();
		if (*p == '{') return parse_obj();
		if (*p == '[') return parse_arr();
		if (*p == '"') { Json j; j.type = Json::STR; j.str = parse_str(); return j; }
		if (!strncmp(p, "true", 4)) { p += 4; Json j; j.type = Json::BOOL; j.b = true; return j; }
		if (!strncmp(p, "false", 5)) { p += 5; Json j; j.type = Json::BOOL; j.b = false; return j; }
		if (!strncmp(p, "null", 4)) { p += 4; return Json(); }
		Json j; j.type = Json::NUM; j.num = strtod(p, (char **)&p); return j;
	}
	std::string parse_str() {
		p++;  // opening quote
		std::string out;
		while (*p && *p != '"') {
			if (*p == '\\') {
				p++;
				switch (*p) {
					case 'n': out += '\n'; break;
					case 't': out += '\t'; break;
					case 'r': out += '\r'; break;
					case 'u': {
						// Fixtures carry UTF-8 paths; node emits them raw, but be safe.
						unsigned cp = (unsigned)strtoul(std::string(p + 1, p + 5).c_str(), nullptr, 16);
						p += 4;
						if (cp < 0x80) out += (char)cp;
						else if (cp < 0x800) { out += (char)(0xc0 | (cp >> 6)); out += (char)(0x80 | (cp & 0x3f)); }
						else { out += (char)(0xe0 | (cp >> 12)); out += (char)(0x80 | ((cp >> 6) & 0x3f)); out += (char)(0x80 | (cp & 0x3f)); }
						break;
					}
					default: out += *p;
				}
				p++;
			} else out += *p++;
		}
		p++;  // closing quote
		return out;
	}
	Json parse_obj() {
		Json j; j.type = Json::OBJ; p++;
		ws();
		if (*p == '}') { p++; return j; }
		while (*p) {
			ws();
			std::string k = parse_str();
			ws(); p++;  // ':'
			j.obj[k] = parse();
			ws();
			if (*p == ',') { p++; continue; }
			if (*p == '}') { p++; break; }
			break;
		}
		return j;
	}
	Json parse_arr() {
		Json j; j.type = Json::ARR; p++;
		ws();
		if (*p == ']') { p++; return j; }
		while (*p) {
			j.arr.push_back(parse());
			ws();
			if (*p == ',') { p++; continue; }
			if (*p == ']') { p++; break; }
			break;
		}
		return j;
	}
};

// Whole-file read. Returns false when the fixture is missing, so a caller can say
// so plainly instead of asserting against an empty document and passing.
inline bool read_text_file(const std::string &path, std::string &out) {
	std::ifstream f(path);
	if (!f) return false;
	std::stringstream ss;
	ss << f.rdbuf();
	out = ss.str();
	return true;
}

#endif // HYPEROVERLAY_TEST_JSON_MINI_H