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)
# Moar warnings
set (CMAKE_CXX_STANDARD 11)
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
# -Wunused-function is pretty annoying here, as everything is static
set (wdisabled "-Wno-unused-function")
@@ -104,6 +105,7 @@ else ()
add_executable (test-lxdrgen-cpp tests/lxdrgen.cpp
${lxdrgen_base}.cpp tools/lxdrgen-cpp-posix.cpp)
endif ()
target_link_libraries (test-lxdrgen-cpp ${common_libraries})
target_include_directories (test-lxdrgen-cpp PUBLIC ${PROJECT_BINARY_DIR})
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.
lxdrgen-cpp.awk::
lxdrgen-cpp-win32.cpp::
lxdrgen-cpp-posix.cpp::
lxdrgen-cpp-qt.cpp::
lxdrgen-cpp-win32.cpp::
LibertyXDR backend for C++, primarily targeting Win32 and its wide strings.
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}
COMMENT "Generating Windows program icon" VERBATIM)
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)
{
self->alloc = 16;
self->line = xcalloc (sizeof *self->line, self->alloc);
self->w = xcalloc (sizeof *self->w, self->alloc);
self->line = xcalloc (self->alloc, sizeof *self->line);
self->w = xcalloc (self->alloc, sizeof *self->w);
self->len = 0;
self->point = 0;
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_INIT_SIZED(a, n) \
BLOCK_START \
(a) = xcalloc (sizeof *(a), (a ## _alloc) = (n)); \
(a) = xcalloc ((a ## _alloc) = (n), sizeof *(a)); \
(a ## _len) = 0; \
BLOCK_END
#define ARRAY_INIT(a) ARRAY_INIT_SIZED (a, 16)
@@ -398,7 +398,7 @@ strv_make (void)
struct strv self;
self.alloc = 4;
self.len = 0;
self.vector = xcalloc (sizeof *self.vector, self.alloc);
self.vector = xcalloc (self.alloc, sizeof *self.vector);
return self;
}
@@ -4833,7 +4833,8 @@ config_item_write_kv_pair (struct config_writer *self,
str_append_printf (self->output,
"%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++)
if (!config_tokenizer_is_word_char (*p))
can_use_word = false;

View File

@@ -636,7 +636,7 @@ test_config_validate_nonnegative
return false;
}
static struct config_schema g_config_test[] =
static const struct config_schema g_config_test[] =
{
{ .name = "foo",
.comment = "baz",
@@ -647,7 +647,7 @@ static struct config_schema g_config_test[] =
.type = CONFIG_ITEM_INTEGER,
.validate = test_config_validate_nonnegative,
.default_ = "1" },
{ .name = "foobar",
{ .name = "123",
.type = CONFIG_ITEM_STRING,
.default_ = "\"qux\\x01`\" \"\"`a`" },
{}
@@ -676,10 +676,11 @@ test_config (void)
config_item_destroy (invalid);
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 ();
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);
hard_assert (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.
#
# 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
#
# 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 "// appending it to the end of a byte stream."
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}"
print "\tdata = binary.BigEndian.AppendUint32(data, uint32(len(s)))"
@@ -301,9 +301,11 @@ function codegen_marshal(type, f, marshal) {
"\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"])
f = "s." camel
if (isaccessor)
f = f "()"
if (!d["isarray"]) {
append(cg, "marshal",
"\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) {
codegen_struct_field_marshal(d, cg)
codegen_struct_field_marshal(d, cg, 0)
camel = snaketocamel(d["name"])
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) {
codegen_struct_field_marshal(d, cg)
function codegen_struct_tag(d, cg) {
codegen_struct_field_marshal(d, cg, 1)
camel = snaketocamel(d["name"])
f = "s." camel
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.
# Do not serialize or deserialize here,
# that is already done by the containing union.
}
function codegen_struct(name, cg, gotype) {
@@ -450,13 +448,18 @@ function codegen_union_struct(name, casename, cg, scg, structname, init) {
structname = name snaketocamel(casename)
codegen_struct(structname, scg)
init = CodegenGoType[structname] "{" cg["tagname"] \
": " decapitalize(cg["tagname"]) "}"
print "func (u *" CodegenGoType[structname] ") " cg["tagname"] "() " \
CodegenGoType[cg["tagtype"]] " {"
print "\treturn " CodegenGoType[cg["tagtype"]] snaketocamel(casename)
print "}"
print ""
init = CodegenGoType[structname] "{}"
append(cg, "unmarshal",
"\tcase " CodegenGoType[cg["tagtype"]] snaketocamel(casename) ":\n" \
"\t\ts := " init "\n" \
"\t\terr = json.Unmarshal(data, &s)\n" \
"\t\tu.Interface = &s\n")
"\t\tu.Variant = &s\n")
append(cg, "serialize",
"\tcase *" CodegenGoType[structname] ":\n" \
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" \
"\t\ts := " init "\n" \
indent(sprintf(CodegenDeserialize[structname], "s")) \
"\t\tu.Interface = &s\n")
"\t\tu.Variant = &s\n")
}
function codegen_union(name, cg, exhaustive, gotype, tagvar) {
gotype = PrefixCamel name
# This must be a struct, so that UnmarshalJSON can create concrete types.
print "type " gotype " struct {"
print "\tInterface any"
print "\tVariant interface {"
print "\t\t" cg["tagname"] "() " CodegenGoType[cg["tagtype"]]
print "\t}"
print "}"
print ""
# This cannot be a pointer method, it wouldn't work recursively.
CodegenIsMarshaler[name] = 1
print "func (u " gotype ") MarshalJSON() ([]byte, error) {"
print "\treturn u.Interface.(json.Marshaler).MarshalJSON()"
print "\treturn u.Variant.(json.Marshaler).MarshalJSON()"
print "}"
print ""
@@ -499,13 +505,15 @@ function codegen_union(name, cg, exhaustive, gotype, tagvar) {
print "}"
print ""
# XXX: Consider changing the interface into an AppendTo/ConsumeFrom one,
# that would eliminate these type case switches entirely.
# On the other hand, it would make it possible to send unsuitable structs.
# XXX: Consider rather testing the type for having an AppendTo method,
# which would eliminate this type case switch entirely.
print "func (u *" gotype ") AppendTo(data []byte) ([]byte, bool) {"
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 "\t\t_ = union"
print "\t\treturn nil, false"
print "\t}"
print "\treturn data, ok"