Compare commits

...

9 Commits

Author SHA1 Message Date
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
75fc6f1c37 const-qualify configuration schema items
All checks were successful
Alpine 3.20 Success
2024-08-08 08:53:49 +02:00
8a8437634a MPD client: fix argument quoting
All checks were successful
Alpine 3.20 Success
2024-08-07 22:04:00 +02:00
e78b410a6a MPD client: save the protocol version 2024-08-07 22:03:08 +02:00
bf44e827e8 liberty-xui: mention libgrapheme 2024-07-10 17:38:39 +02:00
8386af0420 Silence an OpenBSD linker warning
All checks were successful
Alpine 3.19 Success
2024-04-10 17:54:34 +02:00
5 changed files with 61 additions and 51 deletions

View File

@@ -1381,7 +1381,7 @@ struct mpd_client
// Protocol: // Protocol:
bool got_hello; ///< Got the OK MPD hello message char *got_hello; ///< Version from OK MPD hello message
bool idling; ///< Sent idle as the last command bool idling; ///< Sent idle as the last command
unsigned idling_subsystems; ///< Subsystems we're idling for unsigned idling_subsystems; ///< Subsystems we're idling for
@@ -1482,7 +1482,7 @@ mpd_client_reset (struct mpd_client *self)
str_reset (&self->read_buffer); str_reset (&self->read_buffer);
str_reset (&self->write_buffer); str_reset (&self->write_buffer);
self->got_hello = false; cstr_set (&self->got_hello, NULL);
self->idling = false; self->idling = false;
self->idling_subsystems = 0; self->idling_subsystems = 0;
self->in_list = false; self->in_list = false;
@@ -1549,7 +1549,8 @@ mpd_client_parse_hello (struct mpd_client *self, const char *line)
// TODO: call "on_connected" now. We should however also set up a timer // TODO: call "on_connected" now. We should however also set up a timer
// so that we don't wait on this message forever. // so that we don't wait on this message forever.
return self->got_hello = true; cstr_set (&self->got_hello, xstrdup (line + sizeof hello - 1));
return true;
} }
static bool static bool
@@ -1634,30 +1635,30 @@ mpd_client_on_ready (const struct pollfd *pfd, void *user_data)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static bool
mpd_client_must_quote_char (char c)
{
return (unsigned char) c <= ' ' || c == '"' || c == '\'';
}
static bool static bool
mpd_client_must_quote (const char *s) mpd_client_must_quote (const char *s)
{ {
if (!*s) if (!*s)
return true; return true;
for (; *s; s++) for (; *s; s++)
if (mpd_client_must_quote_char (*s)) if ((unsigned char) *s <= ' ' || *s == '"' || *s == '\'')
return true; return true;
return false; return false;
} }
static bool
mpd_client_must_escape_in_quote (char c)
{
return c == '"' || c == '\'' || c == '\\';
}
static void static void
mpd_client_quote (const char *s, struct str *output) mpd_client_quote (const char *s, struct str *output)
{ {
str_append_c (output, '"'); str_append_c (output, '"');
for (; *s; s++) for (; *s; s++)
{ {
if (mpd_client_must_quote_char (*s)) if (mpd_client_must_escape_in_quote (*s))
str_append_c (output, '\\'); str_append_c (output, '\\');
str_append_c (output, *s); str_append_c (output, *s);
} }

View File

@@ -24,12 +24,12 @@
// It is surprisingly hard to find a good library to handle Unicode shenanigans, // It is surprisingly hard to find a good library to handle Unicode shenanigans,
// and there's enough of those for it to be impractical to reimplement them. // and there's enough of those for it to be impractical to reimplement them.
// //
// GLib ICU libunistring utf8proc // GLib ICU libunistring utf8proc libgrapheme
// Decently sized . . x x // Decently sized . . x x x
// Grapheme breaks . x . x // Grapheme breaks . x . x x
// Character width x . x x // Character width x . x x .
// Locale handling . . x . // Locale handling . . x . .
// Liberal license . x . x // Liberal license . x . x x
// //
// Also note that the ICU API is icky and uses UTF-16 for its primary encoding. // Also note that the ICU API is icky and uses UTF-16 for its primary encoding.
// //
@@ -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

@@ -292,7 +292,8 @@ xreallocarray (void *o, size_t n, size_t m)
static char * static char *
xstrdup (const char *s) xstrdup (const char *s)
{ {
return strcpy (xmalloc (strlen (s) + 1), s); size_t len = strlen (s) + 1;
return memcpy (xmalloc (len), s, len);
} }
static char * static char *
@@ -315,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)
@@ -397,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;
} }
@@ -4500,7 +4501,7 @@ struct config_item
} }
value; ///< The value of this item value; ///< The value of this item
struct config_schema *schema; ///< Schema describing this value const struct config_schema *schema; ///< Schema describing this value
void *user_data; ///< User value attached by schema owner void *user_data; ///< User value attached by schema owner
}; };
@@ -4652,7 +4653,7 @@ config_item_object (void)
static bool static bool
config_schema_accepts_type config_schema_accepts_type
(struct config_schema *self, enum config_item_type type) (const struct config_schema *self, enum config_item_type type)
{ {
if (self->type == type) if (self->type == type)
return true; return true;
@@ -4665,7 +4666,7 @@ config_schema_accepts_type
static bool static bool
config_item_validate_by_schema (struct config_item *self, config_item_validate_by_schema (struct config_item *self,
struct config_schema *schema, struct error **e) const struct config_schema *schema, struct error **e)
{ {
struct error *error = NULL; struct error *error = NULL;
if (!config_schema_accepts_type (schema, self->type)) if (!config_schema_accepts_type (schema, self->type))
@@ -4686,7 +4687,7 @@ static bool
config_item_set_from (struct config_item *self, struct config_item *source, config_item_set_from (struct config_item *self, struct config_item *source,
struct error **e) struct error **e)
{ {
struct config_schema *schema = self->schema; const struct config_schema *schema = self->schema;
if (!schema) if (!schema)
{ {
// Easy, we don't know what this item is // Easy, we don't know what this item is
@@ -5482,7 +5483,7 @@ end:
/// "user_data" is passed to allow its immediate use in validation callbacks /// "user_data" is passed to allow its immediate use in validation callbacks
static struct config_item * static struct config_item *
config_schema_initialize_item (struct config_schema *schema, config_schema_initialize_item (const struct config_schema *schema,
struct config_item *parent, void *user_data, struct error **warning, struct config_item *parent, void *user_data, struct error **warning,
struct error **e) struct error **e)
{ {
@@ -5539,7 +5540,7 @@ keep_current:
/// Assign schemas and user_data to multiple items at once; /// Assign schemas and user_data to multiple items at once;
/// feel free to copy over and modify to suit your particular needs /// feel free to copy over and modify to suit your particular needs
static void static void
config_schema_apply_to_object (struct config_schema *schema_array, config_schema_apply_to_object (const struct config_schema *schema_array,
struct config_item *object, void *user_data) struct config_item *object, void *user_data)
{ {
while (schema_array->name) while (schema_array->name)

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",

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"