Add a C++ backend for LibertyXDR
Also change the C backend so that it also de/serializes unions without any other fields besides the tag.
This commit is contained in:
		
							parent
							
								
									f78f8a70f1
								
							
						
					
					
						commit
						2edc9c6fd1
					
				| @ -1,5 +1,5 @@ | ||||
| cmake_minimum_required (VERSION 2.8.12) | ||||
| project (liberty C) | ||||
| project (liberty C CXX) | ||||
| 
 | ||||
| # Moar warnings | ||||
| if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC) | ||||
| @ -72,7 +72,7 @@ add_test (test-cmake-parser | ||||
| # Test protocol code generation | ||||
| set (lxdrgen_outputs) | ||||
| set (lxdrgen_base "${PROJECT_BINARY_DIR}/lxdrgen.lxdr") | ||||
| foreach (backend c go mjs swift) | ||||
| foreach (backend c cpp go mjs swift) | ||||
| 	list (APPEND lxdrgen_outputs ${lxdrgen_base}.${backend}) | ||||
| 	add_custom_command (OUTPUT ${lxdrgen_base}.${backend} | ||||
| 		COMMAND env LC_ALL=C awk | ||||
| @ -91,9 +91,21 @@ add_custom_target (test-lxdrgen-outputs ALL DEPENDS ${lxdrgen_outputs}) | ||||
| 
 | ||||
| set_source_files_properties (${lxdrgen_base}.c | ||||
| 	PROPERTIES HEADER_FILE_ONLY TRUE) | ||||
| add_executable (test-lxdrgen tests/lxdrgen.c ${lxdrgen_base}.c) | ||||
| target_include_directories (test-lxdrgen PUBLIC ${PROJECT_BINARY_DIR}) | ||||
| add_test (NAME test-lxdrgen-c COMMAND test-lxdrgen) | ||||
| add_executable (test-lxdrgen-c tests/lxdrgen.c ${lxdrgen_base}.c) | ||||
| target_include_directories (test-lxdrgen-c PUBLIC ${PROJECT_BINARY_DIR}) | ||||
| add_test (NAME test-lxdrgen-c COMMAND test-lxdrgen-c) | ||||
| 
 | ||||
| set_source_files_properties (${lxdrgen_base}.cpp | ||||
| 	PROPERTIES HEADER_FILE_ONLY TRUE) | ||||
| if (WIN32) | ||||
| 	add_executable (test-lxdrgen-cpp tests/lxdrgen.cpp | ||||
| 		${lxdrgen_base}.cpp tools/lxdrgen-cpp-win32.cpp) | ||||
| else () | ||||
| 	add_executable (test-lxdrgen-cpp tests/lxdrgen.cpp | ||||
| 		${lxdrgen_base}.cpp tools/lxdrgen-cpp-posix.cpp) | ||||
| endif () | ||||
| target_include_directories (test-lxdrgen-cpp PUBLIC ${PROJECT_BINARY_DIR}) | ||||
| add_test (NAME test-lxdrgen-cpp COMMAND test-lxdrgen-cpp) | ||||
| 
 | ||||
| find_program (GO_EXECUTABLE go) | ||||
| if (GO_EXECUTABLE) | ||||
|  | ||||
| @ -44,6 +44,12 @@ lxdrgen.awk:: | ||||
| lxdrgen-c.awk:: | ||||
| 	LibertyXDR backend that builds on top of the C pseudolibrary. | ||||
| 
 | ||||
| lxdrgen-cpp.awk:: | ||||
| lxdrgen-cpp-win32.cpp:: | ||||
| lxdrgen-cpp-posix.cpp:: | ||||
| 	LibertyXDR backend for C++, primarily targeting Win32 and its wide strings. | ||||
| 	Link the result together with one of the accompanied source files. | ||||
| 
 | ||||
| lxdrgen-go.awk:: | ||||
| 	LibertyXDR backend for Go, supporting _encoding/json_ interfaces.  It also | ||||
| 	produces optimized JSON marshallers (however, note that the _json.Marshaler_ | ||||
|  | ||||
| @ -53,6 +53,10 @@ test_ser_deser_free (void) | ||||
| 			u->others.bar = str_make (); | ||||
| 			for (int i = rand () % 0x30; i > 0; i--) | ||||
| 				str_append_c (&u->others.bar, 0x30 + i); | ||||
| 			u->others.baz_len = rand () % 0x30; | ||||
| 			u->others.baz = xcalloc (1, u->others.baz_len); | ||||
| 			for (uint32_t i = 0; i < u->others.baz_len; i++) | ||||
| 				u->others.baz[i] = 0x30 + i; | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			u->tag = PROTO_GEN_ENUM_NOTHING; | ||||
| @ -62,6 +66,8 @@ test_ser_deser_free (void) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	a.o.tag = PROTO_GEN_ENUM_NOTHING; | ||||
| 
 | ||||
| 	struct str buf = str_make (); | ||||
| 	hard_assert (proto_gen_struct_serialize (&a, &buf)); | ||||
| 	struct msg_unpacker r = msg_unpacker_make (buf.str, buf.len); | ||||
| @ -92,6 +98,9 @@ test_ser_deser_free (void) | ||||
| 			hard_assert (ua->others.bar.len == ub->others.bar.len); | ||||
| 			hard_assert (!memcmp (ua->others.bar.str, ub->others.bar.str, | ||||
| 				ua->others.bar.len)); | ||||
| 			hard_assert (ua->others.baz_len == ub->others.baz_len); | ||||
| 			hard_assert (!memcmp (ua->others.baz, ub->others.baz, | ||||
| 				ua->others.baz_len)); | ||||
| 			break; | ||||
| 		case PROTO_GEN_ENUM_NOTHING: | ||||
| 			break; | ||||
| @ -100,6 +109,8 @@ test_ser_deser_free (void) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	hard_assert (a.o.tag == b.o.tag); | ||||
| 
 | ||||
| 	// Emulate partially deserialized data to test disposal of that.
 | ||||
| 	for (size_t i = b.u_len - CASES; i < b.u_len; i++) | ||||
| 	{ | ||||
|  | ||||
							
								
								
									
										132
									
								
								tests/lxdrgen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								tests/lxdrgen.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,132 @@ | ||||
| /*
 | ||||
|  * tests/lxdrgen.cpp | ||||
|  * | ||||
|  * Copyright (c) 2023, Přemysl Eric Janouch <p@janouch.name> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| #include "lxdrgen.lxdr.cpp" | ||||
| 
 | ||||
| #include <cstdlib> | ||||
| 
 | ||||
| static void | ||||
| hard_assert (bool condition, const char *description) | ||||
| { | ||||
| 	if (!condition) | ||||
| 	{ | ||||
| 		fprintf (stderr, "assertion failed: %s\n", description); | ||||
| 		abort (); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| #define hard_assert(condition) hard_assert (condition, #condition) | ||||
| 
 | ||||
| int | ||||
| main (int argc, char *argv[]) | ||||
| { | ||||
| 	hard_assert (ProtoGen::VERSION == 1); | ||||
| 
 | ||||
| 	enum { CASES = 3 }; | ||||
| 
 | ||||
| 	ProtoGen::Struct a = {}, b = {}; | ||||
| 	a.u.resize (CASES + rand () % 100); | ||||
| 	for (size_t i = 0; i < a.u.size (); i++) | ||||
| 	{ | ||||
| 		std::unique_ptr<ProtoGen::Union> &u = a.u[i]; | ||||
| 		switch (i % CASES) | ||||
| 		{ | ||||
| 		case 0: | ||||
| 		{ | ||||
| 			auto numbers = new ProtoGen::Union_Numbers (); | ||||
| 			numbers->a = rand () % UINT8_MAX; | ||||
| 			numbers->b = rand () % UINT16_MAX; | ||||
| 			numbers->c = rand () % UINT32_MAX; | ||||
| 			numbers->d = rand () % UINT64_MAX; | ||||
| 			numbers->e = rand () % UINT8_MAX; | ||||
| 			numbers->f = rand () % UINT16_MAX; | ||||
| 			numbers->g = rand () % UINT32_MAX; | ||||
| 			numbers->h = rand () % UINT64_MAX; | ||||
| 			u.reset (numbers); | ||||
| 			break; | ||||
| 		} | ||||
| 		case 1: | ||||
| 		{ | ||||
| 			auto others = new ProtoGen::Union_Others (); | ||||
| 			others->foo = rand () % 2; | ||||
| 			for (int i = rand () % 0x30; i > 0; i--) | ||||
| 				others->bar += 0x30 + i; | ||||
| 			for (int i = rand () % 0x30; i > 0; i--) | ||||
| 				others->baz.push_back (0x30 + i); | ||||
| 			u.reset (others); | ||||
| 			break; | ||||
| 		} | ||||
| 		case 2: | ||||
| 			u.reset (new ProtoGen::Union_Nothing ()); | ||||
| 			break; | ||||
| 		default: | ||||
| 			hard_assert (!"unhandled case"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	a.o.reset (new ProtoGen::Onion_Nothing ()); | ||||
| 
 | ||||
| 	LibertyXDR::Writer buf; | ||||
| 	hard_assert (a.serialize (buf)); | ||||
| 	LibertyXDR::Reader r; | ||||
| 	r.data = buf.data.data (); | ||||
| 	r.length = buf.data.size (); | ||||
| 	hard_assert (b.deserialize (r)); | ||||
| 	hard_assert (!r.length); | ||||
| 
 | ||||
| 	hard_assert (a.u.size () == b.u.size ()); | ||||
| 	for (size_t i = 0; i < a.u.size (); i++) | ||||
| 	{ | ||||
| 		ProtoGen::Union *ua = a.u[i].get (); | ||||
| 		ProtoGen::Union *ub = b.u[i].get (); | ||||
| 		hard_assert (ua->tag == ub->tag); | ||||
| 		switch (ua->tag) | ||||
| 		{ | ||||
| 		case ProtoGen::Enum::NUMBERS: | ||||
| 		{ | ||||
| 			auto a = dynamic_cast<ProtoGen::Union_Numbers *> (ua); | ||||
| 			auto b = dynamic_cast<ProtoGen::Union_Numbers *> (ub); | ||||
| 			hard_assert (a->a == b->a); | ||||
| 			hard_assert (a->b == b->b); | ||||
| 			hard_assert (a->c == b->c); | ||||
| 			hard_assert (a->d == b->d); | ||||
| 			hard_assert (a->e == b->e); | ||||
| 			hard_assert (a->f == b->f); | ||||
| 			hard_assert (a->g == b->g); | ||||
| 			hard_assert (a->h == b->h); | ||||
| 			break; | ||||
| 		} | ||||
| 		case ProtoGen::Enum::OTHERS: | ||||
| 		{ | ||||
| 			auto a = dynamic_cast<ProtoGen::Union_Others *> (ua); | ||||
| 			auto b = dynamic_cast<ProtoGen::Union_Others *> (ub); | ||||
| 			hard_assert (a->foo == b->foo); | ||||
| 			hard_assert (a->bar == b->bar); | ||||
| 			hard_assert (a->baz == b->baz); | ||||
| 			break; | ||||
| 		} | ||||
| 		case ProtoGen::Enum::NOTHING: | ||||
| 			break; | ||||
| 		default: | ||||
| 			hard_assert (!"unexpected case"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	hard_assert (a.o->tag == b.o->tag); | ||||
| 	return 0; | ||||
| } | ||||
| @ -17,6 +17,7 @@ struct Struct { | ||||
| 	case OTHERS: | ||||
| 		bool foo; | ||||
| 		string bar; | ||||
| 		u8 baz<>; | ||||
| 	case NOTHING: | ||||
| 		void; | ||||
| 	} u<>; | ||||
|  | ||||
| @ -222,7 +222,7 @@ function codegen_struct(name, cg,    ctype, funcname) { | ||||
| 		delete cg[i] | ||||
| } | ||||
| 
 | ||||
| function codegen_union_tag(d, cg) { | ||||
| function codegen_union_tag(name, d, cg) { | ||||
| 	cg["tagtype"] = d["type"] | ||||
| 	cg["tagname"] = d["name"] | ||||
| 	append(cg, "fields", "\t" CodegenCType[d["type"]] " " d["name"] ";\n") | ||||
| @ -281,7 +281,7 @@ function codegen_union(name, cg, exhaustive,    f, ctype, funcname) { | ||||
| 
 | ||||
| 		CodegenDispose[name] = "\t" funcname "(&%s);\n" | ||||
| 	} | ||||
| 	if (cg["serialize"]) { | ||||
| 	{ | ||||
| 		funcname = PrefixLower cameltosnake(name) "_serialize" | ||||
| 		print "" | ||||
| 		print "static bool\n" \ | ||||
| @ -299,7 +299,7 @@ function codegen_union(name, cg, exhaustive,    f, ctype, funcname) { | ||||
| 		CodegenSerialize[name] = "\tif (!" funcname "(&%s, w))\n" \ | ||||
| 			"\t\treturn false;\n" | ||||
| 	} | ||||
| 	if (cg["deserialize"]) { | ||||
| 	{ | ||||
| 		funcname = PrefixLower cameltosnake(name) "_deserialize" | ||||
| 		print "" | ||||
| 		print "static bool\n" \ | ||||
|  | ||||
							
								
								
									
										67
									
								
								tools/lxdrgen-cpp-posix.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								tools/lxdrgen-cpp-posix.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| // lxdrgen-cpp-posix.cpp: POSIX support code for lxdrgen-cpp.awk.
 | ||||
| //
 | ||||
| // Copyright (c) 2023, Přemysl Eric Janouch <p@janouch.name>
 | ||||
| // SPDX-License-Identifier: 0BSD
 | ||||
| #include <iconv.h> | ||||
| 
 | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
| 
 | ||||
| // Various BSD derivatives may have a problem here.
 | ||||
| // Linux defines __STDC_ISO_10646__, but also supports "WCHAR_T".
 | ||||
| #ifdef APPLE | ||||
| #define ICONV_WCHAR "UTF-32" | ||||
| #else | ||||
| #define ICONV_WCHAR "WCHAR_T" | ||||
| #endif | ||||
| 
 | ||||
| namespace LibertyXDR { | ||||
| 
 | ||||
| bool utf8_to_wstring(const uint8_t *utf8, size_t length, std::wstring &wide) { | ||||
| 	iconv_t conv = iconv_open(ICONV_WCHAR, "UTF-8"); | ||||
| 	if (conv == (iconv_t) -1) | ||||
| 		return false; | ||||
| 
 | ||||
| 	wchar_t buffer[1024] = {}; | ||||
| 	char *start = (char *) buffer, *out = start, *in = (char *) utf8; | ||||
| 	size_t available = sizeof buffer; | ||||
| 	wide.clear(); | ||||
| 	while (iconv(conv, &in, &length, &out, &available) == (size_t) -1) { | ||||
| 		if (errno != E2BIG) { | ||||
| 			iconv_close(conv); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		wide.append(buffer, (out - start) / sizeof *buffer); | ||||
| 		out = start; | ||||
| 		available = sizeof buffer; | ||||
| 	} | ||||
| 	wide.append(buffer, (out - start) / sizeof *buffer); | ||||
| 	iconv_close(conv); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool wstring_to_utf8(const std::wstring &wide, std::string &utf8) { | ||||
| 	iconv_t conv = iconv_open("UTF-8", ICONV_WCHAR); | ||||
| 	if (conv == (iconv_t) -1) | ||||
| 		return false; | ||||
| 
 | ||||
| 	char buffer[1024] = {}, *out = buffer, *in = (char *) wide.data(); | ||||
| 	size_t available = sizeof buffer, length = wide.size() * sizeof wide[0]; | ||||
| 	utf8.clear(); | ||||
| 	while (iconv(conv, &in, &length, &out, &available) == (size_t) -1) { | ||||
| 		if (errno != E2BIG) { | ||||
| 			iconv_close(conv); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		utf8.append(buffer, out - buffer); | ||||
| 		out = buffer; | ||||
| 		available = sizeof buffer; | ||||
| 	} | ||||
| 	utf8.append(buffer, out - buffer); | ||||
| 	iconv_close(conv); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| } // namespace LibertyXDR
 | ||||
							
								
								
									
										47
									
								
								tools/lxdrgen-cpp-win32.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								tools/lxdrgen-cpp-win32.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| // lxdrgen-cpp-win32.cpp: Win32 support code for lxdrgen-cpp.awk.
 | ||||
| //
 | ||||
| // Copyright (c) 2023, Přemysl Eric Janouch <p@janouch.name>
 | ||||
| // SPDX-License-Identifier: 0BSD
 | ||||
| #include <windows.h> | ||||
| 
 | ||||
| #include <climits> | ||||
| #include <cstdint> | ||||
| #include <string> | ||||
| 
 | ||||
| namespace LibertyXDR { | ||||
| 
 | ||||
| bool utf8_to_wstring(const uint8_t *utf8, size_t length, std::wstring &wide) { | ||||
| 	wide.clear(); | ||||
| 	if (!length) | ||||
| 		return true; | ||||
| 	if (length > INT_MAX) | ||||
| 		return false; | ||||
| 
 | ||||
| 	int size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, | ||||
| 		(LPCCH) utf8, length, nullptr, 0); | ||||
| 	if (size <= 0) | ||||
| 		return false; | ||||
| 
 | ||||
| 	wide.resize(size); | ||||
| 	return !MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, | ||||
| 		(LPCCH) utf8, length, wide.data(), size); | ||||
| } | ||||
| 
 | ||||
| bool wstring_to_utf8(const std::wstring &wide, std::string &utf8) { | ||||
| 	utf8.clear(); | ||||
| 	if (wide.empty()) | ||||
| 		return true; | ||||
| 	if (wide.size() > INT_MAX) | ||||
| 		return false; | ||||
| 
 | ||||
| 	int size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, | ||||
| 		(LPCWCH) wide.data(), wide.size(), nullptr, 0, NULL, NULL); | ||||
| 	if (size <= 0) | ||||
| 		return false; | ||||
| 
 | ||||
| 	utf8.resize(size); | ||||
| 	return !WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, | ||||
| 		(LPCWCH) wide.data(), wide.size(), utf8.data(), size, NULL, NULL); | ||||
| } | ||||
| 
 | ||||
| } // namespace LibertyXDR
 | ||||
							
								
								
									
										350
									
								
								tools/lxdrgen-cpp.awk
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								tools/lxdrgen-cpp.awk
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,350 @@ | ||||
| # lxdrgen-cpp.awk: C++ backend for lxdrgen.awk. | ||||
| # | ||||
| # This backend is intended for Windows, it just happens to have a fallback | ||||
| # that will probably work on Unices, of which we make use in tests. | ||||
| # | ||||
| # Copyright (c) 2023, Přemysl Eric Janouch <p@janouch.name> | ||||
| # SPDX-License-Identifier: 0BSD | ||||
| 
 | ||||
| function define_internal(name, ctype) { | ||||
| 	Types[name] = "internal" | ||||
| 	CodegenCType[name] = ctype | ||||
| 	CodegenSerialize[name] = \ | ||||
| 		"\tw.append(%s);\n" | ||||
| 	CodegenDeserialize[name] = \ | ||||
| 		"\tif (!r.read(%s))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| } | ||||
| 
 | ||||
| function define_int(shortname, ctype) { | ||||
| 	define_internal(shortname, ctype) | ||||
| } | ||||
| 
 | ||||
| function define_sint(size) { define_int("i" size, "int" size "_t") } | ||||
| function define_uint(size) { define_int("u" size, "uint" size "_t") } | ||||
| 
 | ||||
| function codegen_begin() { | ||||
| 	define_sint("8") | ||||
| 	define_sint("16") | ||||
| 	define_sint("32") | ||||
| 	define_sint("64") | ||||
| 	define_uint("8") | ||||
| 	define_uint("16") | ||||
| 	define_uint("32") | ||||
| 	define_uint("64") | ||||
| 
 | ||||
| 	define_internal("string", "std::wstring") | ||||
| 	define_internal("bool", "bool") | ||||
| 
 | ||||
| 	CodegenSerialize["string"] = \ | ||||
| 		"\tif (!w.append(%s))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| 
 | ||||
| 	print "// Code generated from " FILENAME ". DO NOT EDIT." | ||||
| 	print "" | ||||
| 	print "#include <cstdint>" | ||||
| 	print "#include <memory>" | ||||
| 	print "#include <string>" | ||||
| 	print "#include <vector>" | ||||
| 	print "" | ||||
| 	print "namespace LibertyXDR {" | ||||
| 	print "" | ||||
| 	print "bool utf8_to_wstring(" | ||||
| 	print "\tconst uint8_t *utf8, size_t length, std::wstring &wide);" | ||||
| 	print "bool wstring_to_utf8(" | ||||
| 	print "\tconst std::wstring &wide, std::string &utf8);" | ||||
| 	print "" | ||||
| 	print "struct Reader {" | ||||
| 	print "\tconst uint8_t *data = {};" | ||||
| 	print "\tsize_t length = {};" | ||||
| 	print "" | ||||
| 	print "\ttemplate<typename T> bool read(T &number) {" | ||||
| 	print "\t\tif (length < sizeof number)" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "" | ||||
| 	print "\t\tnumber = 0;" | ||||
| 	print "\t\tfor (size_t i = 0; i < sizeof number; i++) {" | ||||
| 	print "\t\t\tnumber = number << 8 | *data++;" | ||||
| 	print "\t\t\tlength--;" | ||||
| 	print "\t\t}" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool read(bool &boolean) {" | ||||
| 	print "\t\tuint8_t number = 0;" | ||||
| 	print "\t\tif (!read(number))" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "" | ||||
| 	print "\t\tboolean = number != 0;" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool read(std::wstring &string) {" | ||||
| 	print "\t\tuint32_t size = 0;" | ||||
| 	print "\t\tif (!read(size) || size > length)" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "\t\tif (!utf8_to_wstring(data, size, string))" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "" | ||||
| 	print "\t\tdata += size;" | ||||
| 	print "\t\tlength -= size;" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool read(std::vector<uint8_t> &vector) {" | ||||
| 	print "\t\tuint32_t size = 0;" | ||||
| 	print "\t\tif (!read(size) || size > length)" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "\t\tvector.assign(data, data + size);" | ||||
| 	print "" | ||||
| 	print "\t\tdata += size;" | ||||
| 	print "\t\tlength -= size;" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "};" | ||||
| 	print "" | ||||
| 	print "struct Writer {" | ||||
| 	print "\tstd::vector<uint8_t> data;" | ||||
| 	print "" | ||||
| 	print "\ttemplate<typename T> bool append(T number) {" | ||||
| 	print "\t\tuint8_t buffer[sizeof number], *p = buffer + sizeof buffer;" | ||||
| 	print "\t\twhile (p != buffer) {" | ||||
| 	print "\t\t\t*--p = number;" | ||||
| 	print "\t\t\tnumber >>= 8;" | ||||
| 	print "\t\t}" | ||||
| 	print "\t\tdata.insert(data.end(), buffer, buffer + sizeof buffer);" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool append(int8_t number) {" | ||||
| 	print "\t\tdata.push_back(number);" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool append(uint8_t number) {" | ||||
| 	print "\t\tdata.push_back(number);" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool append(bool boolean) {" | ||||
| 	print "\t\treturn append(uint8_t(boolean));" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool append(const std::wstring &string) {" | ||||
| 	print "\t\tif (string.size() > UINT32_MAX)" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "" | ||||
| 	print "\t\tstd::string utf8;" | ||||
| 	print "\t\tif (!wstring_to_utf8(string, utf8))" | ||||
| 	print "\t\t\treturn false;" | ||||
| 	print "" | ||||
| 	print "\t\tappend<uint32_t>(utf8.size());" | ||||
| 	print "\t\tdata.insert(data.end(), utf8.begin(), utf8.end());" | ||||
| 	print "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "};" | ||||
| 	print "" | ||||
| 	print "} // namespace LibertyXDR" | ||||
| 	print "namespace " PrefixCamel " {" | ||||
| } | ||||
| 
 | ||||
| END { | ||||
| 	print "" | ||||
| 	print "} // namespace " PrefixCamel | ||||
| } | ||||
| 
 | ||||
| function codegen_constant(name, value) { | ||||
| 	print "" | ||||
| 	print "enum { " name " = " value " };" | ||||
| } | ||||
| 
 | ||||
| function codegen_enum_value(name, subname, value, cg) { | ||||
| 	append(cg, "fields", "\t" subname " = " value ",\n") | ||||
| } | ||||
| 
 | ||||
| function codegen_enum(name, cg) { | ||||
| 	print "" | ||||
| 	print "enum struct " name " : int8_t {" | ||||
| 	print cg["fields"] "};" | ||||
| 
 | ||||
| 	# XXX: This should also check if it isn't out-of-range for any reason, | ||||
| 	# but our usage of sprintf() stands in the way a bit. | ||||
| 	CodegenSerialize[name] = \ | ||||
| 		"\tw.append(static_cast<int8_t>(%s));\n" | ||||
| 	CodegenDeserialize[name] = \ | ||||
| 		"\t{\n" \ | ||||
| 		"\t\tint8_t v = 0;\n" \ | ||||
| 		"\t\tif (!r.read(v) || !v)\n" \ | ||||
| 		"\t\t\treturn false;\n" \ | ||||
| 		"\t\t%s = static_cast<" name ">(v);\n" \ | ||||
| 		"\t}\n" | ||||
| 
 | ||||
| 	CodegenCType[name] = name | ||||
| 	for (i in cg) | ||||
| 		delete cg[i] | ||||
| } | ||||
| 
 | ||||
| # Some identifiers do not pose a problem in C, but do in our C++. | ||||
| function codegen_struct_sanitize(name) { | ||||
| 	if (name ~ /^(serialize|deserialize)_*$/ || | ||||
| 		name ~ /^(catch|class|delete|except|finally|friend|new|operator)_*$/ || | ||||
| 		name ~ /^(private|protected|public|template|this|throw|try|virtual)_*$/) | ||||
| 		return name "_" | ||||
| 	return name | ||||
| } | ||||
| 
 | ||||
| function codegen_struct_tag(d, cg,    name, f) { | ||||
| 	name = codegen_struct_sanitize(d["name"]) | ||||
| 	f = "this->" name | ||||
| 
 | ||||
| 	append(cg, "serialize", sprintf(CodegenSerialize[d["type"]], f)) | ||||
| 	# Do not deserialize here, that would be out of order. | ||||
| } | ||||
| 
 | ||||
| function codegen_struct_field(d, cg,    name, f, serialize, deserialize) { | ||||
| 	name = codegen_struct_sanitize(d["name"]) | ||||
| 	f = "this->" name | ||||
| 
 | ||||
| 	serialize = CodegenSerialize[d["type"]] | ||||
| 	deserialize = CodegenDeserialize[d["type"]] | ||||
| 	if (!d["isarray"]) { | ||||
| 		append(cg, "fields", | ||||
| 			"\t" CodegenCType[d["type"]] " " name " = {};\n") | ||||
| 		append(cg, "serialize", sprintf(serialize, f)) | ||||
| 		append(cg, "deserialize", sprintf(deserialize, f)) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	append(cg, "fields", | ||||
| 		"\tstd::vector<" CodegenCType[d["type"]] "> " name ";\n") | ||||
| 
 | ||||
| 	# XXX: We should probably pedantically check for overflows. | ||||
| 	append(cg, "serialize", | ||||
| 		sprintf(CodegenSerialize["u32"], "uint32_t(" f ".size())") \ | ||||
| 		"\tfor (const auto &it : " f ")\n" \ | ||||
| 		indent(sprintf(serialize, "it"))) | ||||
| 
 | ||||
| 	if (d["type"] == "u8") { | ||||
| 		append(cg, "deserialize", | ||||
| 			"\tif (!r.read(" f "))\n" \ | ||||
| 			"\t\treturn false;\n") | ||||
| 	} else if (deserialize) { | ||||
| 		append(cg, "deserialize", | ||||
| 			"\t{\n" \ | ||||
| 			"\t\tuint32_t size = 0;\n" \ | ||||
| 			indent(sprintf(CodegenDeserialize["u32"], "size")) \ | ||||
| 			"\t\t" f ".resize(size);\n" \ | ||||
| 			"\t}\n" \ | ||||
| 			"\tfor (auto &it : " f ")\n" \ | ||||
| 			indent(sprintf(deserialize, "it"))) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| function codegen_struct(name, cg) { | ||||
| 	print "" | ||||
| 	print "struct " name " {" | ||||
| 	print cg["fields"] | ||||
| 	print "\tbool serialize(LibertyXDR::Writer &w) const {" | ||||
| 	print indent(cg["serialize"]) "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tbool deserialize([[maybe_unused]] LibertyXDR::Reader &r) {" | ||||
| 	print indent(cg["deserialize"]) "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "};" | ||||
| 
 | ||||
| 	CodegenSerialize[name] = "\tif (!%s->serialize(w))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| 	CodegenDeserialize[name] = "\tif (!%s->deserialize(r))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| 
 | ||||
| 	CodegenCType[name] = name | ||||
| 	for (i in cg) | ||||
| 		delete cg[i] | ||||
| } | ||||
| 
 | ||||
| function codegen_union_tag(name, d, cg,    tagname) { | ||||
| 	cg["tagtype"] = d["type"] | ||||
| 	cg["tagname"] = tagname = codegen_struct_sanitize(d["name"]) | ||||
| 
 | ||||
| 	print "" | ||||
| 	print "struct " name " {" | ||||
| 	print "\t" CodegenCType[d["type"]] " " tagname " = {};" | ||||
| 	print "\tvirtual ~" name "() = 0;" | ||||
| 	print "\tvirtual bool serialize(LibertyXDR::Writer &w) const = 0;" | ||||
| 	print "\tvirtual bool deserialize(LibertyXDR::Reader &r) = 0;" | ||||
| 	print "};" | ||||
| 	print "" | ||||
| 	print name "::~" name "() {}" | ||||
| } | ||||
| 
 | ||||
| function codegen_union_struct(name, casename, cg, scg,     structname) { | ||||
| 	# And thus not all generated structs are present in Types. | ||||
| 	structname = name "_" snaketocamel(casename) | ||||
| 
 | ||||
| 	print "" | ||||
| 	print "struct " structname " : virtual public " name " {" | ||||
| 	print scg["fields"] | ||||
| 	print "\t" structname "() {" | ||||
| 	print "\t\tthis->" cg["tagname"] " = " \ | ||||
| 		CodegenCType[cg["tagtype"]] "::" casename ";" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tvirtual bool serialize(LibertyXDR::Writer &w) const {" | ||||
| 	print indent(scg["serialize"]) "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "" | ||||
| 	print "\tvirtual bool deserialize([[maybe_unused]] LibertyXDR::Reader &r) {" | ||||
| 	print indent(scg["deserialize"]) "\t\treturn true;" | ||||
| 	print "\t}" | ||||
| 	print "};" | ||||
| 
 | ||||
| 	append(cg, "deserialize", | ||||
| 		"\tcase " CodegenCType[cg["tagtype"]] "::" casename ":\n" \ | ||||
| 		"\t\treturn new " structname "();\n") | ||||
| 
 | ||||
| 	CodegenSerialize[structname] = "\tif (!%s->serialize(w))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| 	CodegenDeserialize[structname] = "\tif (!%s->deserialize(r))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| 
 | ||||
| 	CodegenCType[structname] = structname | ||||
| 	for (i in scg) | ||||
| 		delete scg[i] | ||||
| } | ||||
| 
 | ||||
| function codegen_union(name, cg, exhaustive,    ctype) { | ||||
| 	CodegenSerialize[name] = "\tif (!%s->serialize(w))\n" \ | ||||
| 		"\t\treturn false;\n" | ||||
| 
 | ||||
| 	ctype = "std::unique_ptr<" name ">" | ||||
| 	if (cg["deserialize"]) { | ||||
| 		print "" | ||||
| 		print "static " name " *read" name "(" \ | ||||
| 			CodegenCType[cg["tagtype"]] " " cg["tagname"] ") {" | ||||
| 		print "\tswitch (" cg["tagname"] ") {" | ||||
| 		print cg["deserialize"] "\tdefault:" | ||||
| 		print "\t\treturn nullptr;" | ||||
| 		print "\t}" | ||||
| 		print "}" | ||||
| 		print "" | ||||
| 		print "static " ctype " read" name "(LibertyXDR::Reader &r) {" | ||||
| 		print "\tint8_t v = 0;" | ||||
| 		print "\tif (!r.read(v) || !v)" | ||||
| 		print "\t\treturn nullptr;" | ||||
| 		print "" | ||||
| 		print "\t" ctype " result(read" name "(static_cast<" \ | ||||
| 			CodegenCType[cg["tagtype"]] ">(v)));" | ||||
| 		print "\tif (!result || !result->deserialize(r))" | ||||
| 		print "\t\treturn nullptr;" | ||||
| 		print "\treturn result;" | ||||
| 		print "}" | ||||
| 
 | ||||
| 		CodegenDeserialize[name] = "\tif (!(%s = read" name "(r)))\n" \ | ||||
| 			"\t\treturn false;\n" | ||||
| 	} | ||||
| 
 | ||||
| 	CodegenCType[name] = ctype | ||||
| 	for (i in cg) | ||||
| 		delete cg[i] | ||||
| } | ||||
| @ -439,7 +439,7 @@ function codegen_struct(name, cg,    gotype) { | ||||
| 		delete cg[i] | ||||
| } | ||||
| 
 | ||||
| function codegen_union_tag(d, cg) { | ||||
| function codegen_union_tag(name, d, cg) { | ||||
| 	cg["tagtype"] = d["type"] | ||||
| 	cg["tagname"] = snaketocamel(d["name"]) | ||||
| 	# The tag is implied from the type of struct stored in the interface. | ||||
|  | ||||
| @ -183,7 +183,7 @@ function codegen_struct(name, cg) { | ||||
| 		delete cg[i] | ||||
| } | ||||
| 
 | ||||
| function codegen_union_tag(d, cg) { | ||||
| function codegen_union_tag(name, d, cg) { | ||||
| 	cg["tagtype"] = d["type"] | ||||
| 	cg["tagname"] = decapitalize(snaketocamel(d["name"])) | ||||
| } | ||||
|  | ||||
| @ -215,7 +215,7 @@ function codegen_struct(name, cg,    swifttype) { | ||||
| 		delete cg[i] | ||||
| } | ||||
| 
 | ||||
| function codegen_union_tag(d, cg) { | ||||
| function codegen_union_tag(name, d, cg) { | ||||
| 	cg["tagtype"] = d["type"] | ||||
| 	cg["tagname"] = decapitalize(snaketocamel(d["name"])) | ||||
| } | ||||
|  | ||||
| @ -232,7 +232,7 @@ function defunion(    name, tag, tagtype, tagvalue, cg, scg, d, a, i, | ||||
| 
 | ||||
| 	if (Types[tagtype] != "enum") | ||||
| 		fatal("not an enum type: " tagtype) | ||||
| 	codegen_union_tag(tag, cg) | ||||
| 	codegen_union_tag(name, tag, cg) | ||||
| 
 | ||||
| 	split(EnumValues[tagtype], a, SUBSEP) | ||||
| 	for (i in a) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user