Compare commits

..

8 Commits

Author SHA1 Message Date
1930f138d4 IconUtils: add Apple Icon Image format support
All checks were successful
Alpine 3.20 Success
2024-12-17 06:20:12 +01:00
32cbb15266 Serialize integer-ish config keys properly
All checks were successful
Alpine 3.20 Success
2024-12-16 09:09:03 +01:00
149938cc44 lxdrgen-cpp: add a Qt backend
All checks were successful
Alpine 3.20 Success
Motivation: some Android NDKs do not have iconv.
2024-12-15 06:44:06 +01:00
62f8a7d05f lxdrgen-cpp: fix test build on macOS
All checks were successful
Alpine 3.20 Success
2024-12-04 17:44:30 +01:00
492815c8fc lxdrgen-go: fix compatibility with 32-bit targets
All checks were successful
Alpine 3.20 Success
2024-11-09 17:06:46 +01:00
aacf1b1d47 lxdrgen-go: improve usability
All checks were successful
Alpine 3.20 Success
Turning union tags into read-only methods of actual types:
 - eliminates duplicated JSON unmarshalling of tags,
 - makes AppendTo/ConsumeFrom symmetrical in nature,
 - eliminates duplicated AppendTo code,
 - eliminates trivial AppendTo methods for subtypes without fields,
 - gives us an opportunity to use a more specific interface than "any"
   (the type being anonymous is an acknowledged inconvenience).

Implementing our own json.Marshalers some time ago
(for performance reasons) has made this easier to implement.

Also rename "Interface" fields to more suitable "Variant".
2024-11-07 11:01:41 +01:00
49d7cb12bb Fix calloc argument order
All checks were successful
Alpine 3.20 Success
2024-08-08 09:34:33 +02:00
fdf845d0bd const-qualify configuration schema items in tests 2024-08-08 09:21:16 +02:00
8 changed files with 111 additions and 31 deletions

View File

@@ -2,6 +2,7 @@ cmake_minimum_required (VERSION 3.0...3.27)
project (liberty C CXX) project (liberty C CXX)
# Moar warnings # Moar warnings
set (CMAKE_CXX_STANDARD 11)
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC) if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# -Wunused-function is pretty annoying here, as everything is static # -Wunused-function is pretty annoying here, as everything is static
set (wdisabled "-Wno-unused-function") set (wdisabled "-Wno-unused-function")
@@ -104,6 +105,7 @@ else ()
add_executable (test-lxdrgen-cpp tests/lxdrgen.cpp add_executable (test-lxdrgen-cpp tests/lxdrgen.cpp
${lxdrgen_base}.cpp tools/lxdrgen-cpp-posix.cpp) ${lxdrgen_base}.cpp tools/lxdrgen-cpp-posix.cpp)
endif () endif ()
target_link_libraries (test-lxdrgen-cpp ${common_libraries})
target_include_directories (test-lxdrgen-cpp PUBLIC ${PROJECT_BINARY_DIR}) target_include_directories (test-lxdrgen-cpp PUBLIC ${PROJECT_BINARY_DIR})
add_test (NAME test-lxdrgen-cpp COMMAND test-lxdrgen-cpp) add_test (NAME test-lxdrgen-cpp COMMAND test-lxdrgen-cpp)

View File

@@ -45,8 +45,9 @@ lxdrgen-c.awk::
LibertyXDR backend that builds on top of the C pseudolibrary. LibertyXDR backend that builds on top of the C pseudolibrary.
lxdrgen-cpp.awk:: lxdrgen-cpp.awk::
lxdrgen-cpp-win32.cpp::
lxdrgen-cpp-posix.cpp:: lxdrgen-cpp-posix.cpp::
lxdrgen-cpp-qt.cpp::
lxdrgen-cpp-win32.cpp::
LibertyXDR backend for C++, primarily targeting Win32 and its wide strings. LibertyXDR backend for C++, primarily targeting Win32 and its wide strings.
Link the result together with one of the accompanied source files. Link the result together with one of the accompanied source files.

View File

@@ -38,3 +38,47 @@ function (icon_for_win32 ico pngs pngs_raw)
DEPENDS ${pngs} ${pngs_raw} DEPENDS ${pngs} ${pngs_raw}
COMMENT "Generating Windows program icon" VERBATIM) COMMENT "Generating Windows program icon" VERBATIM)
endfunction () endfunction ()
function (icon_to_iconset_size name svg size iconset outputs)
math (EXPR _size2x "${size} * 2")
set (_dimensions "${size}x${size}")
set (_png1x "${iconset}/icon_${_dimensions}.png")
set (_png2x "${iconset}/icon_${_dimensions}@2x.png")
set (${outputs} "${_png1x};${_png2x}" PARENT_SCOPE)
set (_find_program_REQUIRE)
if (NOT ${CMAKE_VERSION} VERSION_LESS 3.18.0)
set (_find_program_REQUIRE REQUIRED)
endif ()
find_program (rsvg_convert_EXECUTABLE rsvg-convert ${_find_program_REQUIRE})
add_custom_command (OUTPUT "${_png1x}" "${_png2x}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${iconset}"
COMMAND ${rsvg_convert_EXECUTABLE} "--output=${_png1x}"
"--width=${size}" "--height=${size}" -- "${svg}"
COMMAND ${rsvg_convert_EXECUTABLE} "--output=${_png2x}"
"--width=${_size2x}" "--height=${_size2x}" -- "${svg}"
DEPENDS "${svg}"
COMMENT "Generating ${name} ${_dimensions} icons" VERBATIM)
endfunction ()
function (icon_to_icns svg output_basename output)
get_filename_component (_name "${output_basename}" NAME_WE)
set (_iconset "${PROJECT_BINARY_DIR}/${_name}.iconset")
set (_icon "${PROJECT_BINARY_DIR}/${output_basename}")
set (${output} "${_icon}" PARENT_SCOPE)
set (_icon_png_list)
foreach (_icon_size 16 32 128 256 512)
icon_to_iconset_size ("${_name}" "${svg}"
"${_icon_size}" "${_iconset}" _icon_pngs)
list (APPEND _icon_png_list ${_icon_pngs})
endforeach ()
# XXX: This will not normally work from within Nix.
add_custom_command (OUTPUT "${_icon}"
COMMAND iconutil -c icns -o "${_icon}" "${_iconset}"
DEPENDS ${_icon_png_list}
COMMENT "Generating ${_name} icon" VERBATIM)
set_source_files_properties ("${_icon}" PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
endfunction ()

View File

@@ -205,8 +205,8 @@ static void
line_editor_start (struct line_editor *self, char prompt) line_editor_start (struct line_editor *self, char prompt)
{ {
self->alloc = 16; self->alloc = 16;
self->line = xcalloc (sizeof *self->line, self->alloc); self->line = xcalloc (self->alloc, sizeof *self->line);
self->w = xcalloc (sizeof *self->w, self->alloc); self->w = xcalloc (self->alloc, sizeof *self->w);
self->len = 0; self->len = 0;
self->point = 0; self->point = 0;
self->prompt = prompt; self->prompt = prompt;

View File

@@ -316,7 +316,7 @@ xstrndup (const char *s, size_t n)
#define ARRAY(type, name) type *name; size_t name ## _len, name ## _alloc; #define ARRAY(type, name) type *name; size_t name ## _len, name ## _alloc;
#define ARRAY_INIT_SIZED(a, n) \ #define ARRAY_INIT_SIZED(a, n) \
BLOCK_START \ BLOCK_START \
(a) = xcalloc (sizeof *(a), (a ## _alloc) = (n)); \ (a) = xcalloc ((a ## _alloc) = (n), sizeof *(a)); \
(a ## _len) = 0; \ (a ## _len) = 0; \
BLOCK_END BLOCK_END
#define ARRAY_INIT(a) ARRAY_INIT_SIZED (a, 16) #define ARRAY_INIT(a) ARRAY_INIT_SIZED (a, 16)
@@ -398,7 +398,7 @@ strv_make (void)
struct strv self; struct strv self;
self.alloc = 4; self.alloc = 4;
self.len = 0; self.len = 0;
self.vector = xcalloc (sizeof *self.vector, self.alloc); self.vector = xcalloc (self.alloc, sizeof *self.vector);
return self; return self;
} }
@@ -4833,7 +4833,8 @@ config_item_write_kv_pair (struct config_writer *self,
str_append_printf (self->output, str_append_printf (self->output,
"%s# %s\n", indent, value->schema->comment); "%s# %s\n", indent, value->schema->comment);
bool can_use_word = true; char *end = NULL;
bool can_use_word = ((void) strtoll (key, &end, 10), end == key);
for (const char *p = key; *p; p++) for (const char *p = key; *p; p++)
if (!config_tokenizer_is_word_char (*p)) if (!config_tokenizer_is_word_char (*p))
can_use_word = false; can_use_word = false;

View File

@@ -636,7 +636,7 @@ test_config_validate_nonnegative
return false; return false;
} }
static struct config_schema g_config_test[] = static const struct config_schema g_config_test[] =
{ {
{ .name = "foo", { .name = "foo",
.comment = "baz", .comment = "baz",
@@ -647,7 +647,7 @@ static struct config_schema g_config_test[] =
.type = CONFIG_ITEM_INTEGER, .type = CONFIG_ITEM_INTEGER,
.validate = test_config_validate_nonnegative, .validate = test_config_validate_nonnegative,
.default_ = "1" }, .default_ = "1" },
{ .name = "foobar", { .name = "123",
.type = CONFIG_ITEM_STRING, .type = CONFIG_ITEM_STRING,
.default_ = "\"qux\\x01`\" \"\"`a`" }, .default_ = "\"qux\\x01`\" \"\"`a`" },
{} {}
@@ -676,10 +676,11 @@ test_config (void)
config_item_destroy (invalid); config_item_destroy (invalid);
hard_assert (!strcmp ("qux\001`a", hard_assert (!strcmp ("qux\001`a",
config_item_get (config.root, "top.foobar", NULL)->value.string.str)); config_item_get (config.root, "top.123", NULL)->value.string.str));
struct str s = str_make (); struct str s = str_make ();
config_item_write (config.root, true, &s); config_item_write (config.root, true, &s);
print_debug ("%s", s.str);
struct config_item *parsed = config_item_parse (s.str, s.len, false, NULL); struct config_item *parsed = config_item_parse (s.str, s.len, false, NULL);
hard_assert (parsed); hard_assert (parsed);
config_item_destroy (parsed); config_item_destroy (parsed);

23
tools/lxdrgen-cpp-qt.cpp Normal file
View File

@@ -0,0 +1,23 @@
// lxdrgen-cpp-qt.cpp: Qt support code for lxdrgen-cpp.awk.
//
// Copyright (c) 2024, Přemysl Eric Janouch <p@janouch.name>
// SPDX-License-Identifier: 0BSD
#include <QString>
#include <string>
namespace LibertyXDR {
bool utf8_to_wstring(const uint8_t *utf8, size_t length, std::wstring &wide) {
QByteArrayView view(reinterpret_cast<const char *>(utf8), length);
if (!view.isValidUtf8())
return false;
wide = QString::fromUtf8(view).toStdWString();
return true;
}
bool wstring_to_utf8(const std::wstring &wide, std::string &utf8) {
utf8 = QString::fromStdWString(wide).toUtf8().toStdString();
return true;
}
} // namespace LibertyXDR

View File

@@ -1,6 +1,6 @@
# lxdrgen-go.awk: Go backend for lxdrgen.awk. # lxdrgen-go.awk: Go backend for lxdrgen.awk.
# #
# Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name> # Copyright (c) 2022 - 2024, Přemysl Eric Janouch <p@janouch.name>
# SPDX-License-Identifier: 0BSD # SPDX-License-Identifier: 0BSD
# #
# This backend also enables proxying to other endpoints using JSON. # This backend also enables proxying to other endpoints using JSON.
@@ -145,7 +145,7 @@ function codegen_begin( funcname) {
print "// " funcname " tries to serialize a string value," print "// " funcname " tries to serialize a string value,"
print "// appending it to the end of a byte stream." print "// appending it to the end of a byte stream."
print "func " funcname "(data []byte, s string) ([]byte, bool) {" print "func " funcname "(data []byte, s string) ([]byte, bool) {"
print "\tif len(s) > math.MaxUint32 {" print "\tif int64(len(s)) > math.MaxUint32 {"
print "\t\treturn nil, false" print "\t\treturn nil, false"
print "\t}" print "\t}"
print "\tdata = binary.BigEndian.AppendUint32(data, uint32(len(s)))" print "\tdata = binary.BigEndian.AppendUint32(data, uint32(len(s)))"
@@ -301,9 +301,11 @@ function codegen_marshal(type, f, marshal) {
"\t}\n" "\t}\n"
} }
function codegen_struct_field_marshal(d, cg, camel, f, marshal) { function codegen_struct_field_marshal(d, cg, isaccessor, camel, f, marshal) {
camel = snaketocamel(d["name"]) camel = snaketocamel(d["name"])
f = "s." camel f = "s." camel
if (isaccessor)
f = f "()"
if (!d["isarray"]) { if (!d["isarray"]) {
append(cg, "marshal", append(cg, "marshal",
"\tb = append(b, `,\"" decapitalize(camel) "\":`...)\n" \ "\tb = append(b, `,\"" decapitalize(camel) "\":`...)\n" \
@@ -333,7 +335,7 @@ function codegen_struct_field_marshal(d, cg, camel, f, marshal) {
} }
function codegen_struct_field(d, cg, camel, f, serialize, deserialize) { function codegen_struct_field(d, cg, camel, f, serialize, deserialize) {
codegen_struct_field_marshal(d, cg) codegen_struct_field_marshal(d, cg, 0)
camel = snaketocamel(d["name"]) camel = snaketocamel(d["name"])
f = "s." camel f = "s." camel
@@ -384,15 +386,11 @@ function codegen_struct_field(d, cg, camel, f, serialize, deserialize) {
} }
} }
function codegen_struct_tag(d, cg, camel, f) { function codegen_struct_tag(d, cg) {
codegen_struct_field_marshal(d, cg) codegen_struct_field_marshal(d, cg, 1)
camel = snaketocamel(d["name"]) # Do not serialize or deserialize here,
f = "s." camel # that is already done by the containing union.
append(cg, "fields", "\t" camel " " CodegenGoType[d["type"]] \
" `json:\"" decapitalize(camel) "\"`\n")
append(cg, "serialize", sprintf(CodegenSerialize[d["type"]], f))
# Do not deserialize here, that is already done by the containing union.
} }
function codegen_struct(name, cg, gotype) { function codegen_struct(name, cg, gotype) {
@@ -450,13 +448,18 @@ function codegen_union_struct(name, casename, cg, scg, structname, init) {
structname = name snaketocamel(casename) structname = name snaketocamel(casename)
codegen_struct(structname, scg) codegen_struct(structname, scg)
init = CodegenGoType[structname] "{" cg["tagname"] \ print "func (u *" CodegenGoType[structname] ") " cg["tagname"] "() " \
": " decapitalize(cg["tagname"]) "}" CodegenGoType[cg["tagtype"]] " {"
print "\treturn " CodegenGoType[cg["tagtype"]] snaketocamel(casename)
print "}"
print ""
init = CodegenGoType[structname] "{}"
append(cg, "unmarshal", append(cg, "unmarshal",
"\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \ "\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \
"\t\ts := " init "\n" \ "\t\ts := " init "\n" \
"\t\terr = json.Unmarshal(data, &s)\n" \ "\t\terr = json.Unmarshal(data, &s)\n" \
"\t\tu.Interface = &s\n") "\t\tu.Variant = &s\n")
append(cg, "serialize", append(cg, "serialize",
"\tcase *" CodegenGoType[structname] ":\n" \ "\tcase *" CodegenGoType[structname] ":\n" \
indent(sprintf(CodegenSerialize[structname], "union"))) indent(sprintf(CodegenSerialize[structname], "union")))
@@ -464,20 +467,23 @@ function codegen_union_struct(name, casename, cg, scg, structname, init) {
"\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \ "\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \
"\t\ts := " init "\n" \ "\t\ts := " init "\n" \
indent(sprintf(CodegenDeserialize[structname], "s")) \ indent(sprintf(CodegenDeserialize[structname], "s")) \
"\t\tu.Interface = &s\n") "\t\tu.Variant = &s\n")
} }
function codegen_union(name, cg, exhaustive, gotype, tagvar) { function codegen_union(name, cg, exhaustive, gotype, tagvar) {
gotype = PrefixCamel name gotype = PrefixCamel name
# This must be a struct, so that UnmarshalJSON can create concrete types.
print "type " gotype " struct {" print "type " gotype " struct {"
print "\tInterface any" print "\tVariant interface {"
print "\t\t" cg["tagname"] "() " CodegenGoType[cg["tagtype"]]
print "\t}"
print "}" print "}"
print "" print ""
# This cannot be a pointer method, it wouldn't work recursively. # This cannot be a pointer method, it wouldn't work recursively.
CodegenIsMarshaler[name] = 1 CodegenIsMarshaler[name] = 1
print "func (u " gotype ") MarshalJSON() ([]byte, error) {" print "func (u " gotype ") MarshalJSON() ([]byte, error) {"
print "\treturn u.Interface.(json.Marshaler).MarshalJSON()" print "\treturn u.Variant.(json.Marshaler).MarshalJSON()"
print "}" print "}"
print "" print ""
@@ -499,13 +505,15 @@ function codegen_union(name, cg, exhaustive, gotype, tagvar) {
print "}" print "}"
print "" print ""
# XXX: Consider changing the interface into an AppendTo/ConsumeFrom one, # XXX: Consider rather testing the type for having an AppendTo method,
# that would eliminate these type case switches entirely. # which would eliminate this type case switch entirely.
# On the other hand, it would make it possible to send unsuitable structs.
print "func (u *" gotype ") AppendTo(data []byte) ([]byte, bool) {" print "func (u *" gotype ") AppendTo(data []byte) ([]byte, bool) {"
print "\tok := true" print "\tok := true"
print "\tswitch union := u.Interface.(type) {" print sprintf(CodegenSerialize[cg["tagtype"]],
"u.Variant." cg["tagname"] "()") \
"\tswitch union := u.Variant.(type) {"
print cg["serialize"] "\tdefault:" print cg["serialize"] "\tdefault:"
print "\t\t_ = union"
print "\t\treturn nil, false" print "\t\treturn nil, false"
print "\t}" print "\t}"
print "\treturn data, ok" print "\treturn data, ok"