Project

General

Profile

Feature #470 » 0003-Network-protocol-strvec-field-type.patch

main - Alina Lenk, 04/18/2024 04:13 PM

View differences:

common/generate_packets.py
raise ValueError("not a valid string type")
if public_type != "char":
raise ValueError(f"string dataio type with non-char public type: {public_type!r}")
raise ValueError(f"string type with illegal public type: {public_type!r}")
super().__init__(dataio_type, public_type, size)
......
}}
"""
DEFAULT_REGISTRY.dataio_types["string"] = DEFAULT_REGISTRY.dataio_types["estring"] = partial(NeedSizeType, cls = StringType)
class MemoryType(SizedType):
"""Type information for a memory field"""
......
)
def get_code_handle_param(self, location: Location) -> str:
# add "const" if top level
pre = "" if location.depth else "const "
return pre + self.elem.get_code_handle_param(location.deeper(f"*{location}"))
# Note: If we're fine with writing `foo_t const *fieldname`,
# we'd only need one case, .deeper(f"const *{location}")
if not location.depth:
# foo_t fieldname ~> const foo_t *fieldname
return "const " + self.elem.get_code_handle_param(location.deeper(f"*{location}"))
else:
# const foo_t *fieldname ~> const foo_t *const *fieldname
# the final * is already part of {location}
return self.elem.get_code_handle_param(location.deeper(f"*const {location}"))
def get_code_init(self, location: Location) -> str:
if not self.complex:
......
# can't use direct assignment to bit-copy a raw array,
# even if our type is not complex
inner_copy = prefix(" ", self.elem.get_code_copy(location.sub, dest, src))
# FIXME: can't use self.size.real; have to use actual_for(src context)
return f"""\
{{
int {location.index};
......
return f"{self.elem}[{self.size}]"
class StrvecType(FieldType):
"""Type information for a string vector field"""
dataio_type: str
"""How fields of this type are transmitted over network"""
public_type: str
"""How fields of this type are represented in C code"""
complex: bool = True
def __init__(self, dataio_type: str, public_type: str):
if dataio_type not in ("string", "estring"):
raise ValueError("not a valid strvec type")
if public_type != "struct strvec":
raise ValueError(f"strvec type with illegal public type: {public_type!r}")
self.dataio_type = dataio_type
self.public_type = public_type
def get_code_declaration(self, location: Location) -> str:
return f"""\
{self.public_type} *{location};
"""
def get_code_handle_param(self, location: Location) -> str:
if not location.depth:
return f"const {self.public_type} *{location}"
else:
# const struct strvec *const *fieldname
# the final * is already part of {location}
# initial const gets added from outside
return f"{self.public_type} *const {location}"
def get_code_init(self, location: Location) -> str:
# we're always allocating our vectors, even if they're empty
return f"""\
{location} = strvec_new();
"""
def get_code_fill(self, location: Location) -> str:
"""Generate a code snippet shallow-copying a value of this type from
dsend arguments into a packet struct."""
# safety: the packet's contents will not be modified without cloning
# it first, so discarding 'const' qualifier here is safe
return f"""\
real_packet->{location} = (struct strvec *) {location};
"""
def get_code_copy(self, location: Location, dest: str, src: str) -> str:
# dest is initialized by us ~> not null
# src might be a packet passed in from outside ~> could be null
return f"""\
if ({src}->{location}) {{
strvec_copy({dest}->{location}, {src}->{location});
}} else {{
strvec_clear({dest}->{location});
}}
"""
def get_code_free(self, location: Location) -> str:
return f"""\
if ({location}) {{
strvec_destroy({location});
{location} = nullptr;
}}
"""
def get_code_hash(self, location: Location) -> str:
raise ValueError(f"hash not supported for strvec type {self} in field {location.name}")
def get_code_cmp(self, location: Location) -> str:
return f"""\
differ = !are_strvecs_equal(old->{location}, real_packet->{location});
"""
def _get_code_put_full(self, location: Location) -> str:
# Note: strictly speaking, we could allow size == MAX_UINT16,
# but we might want to use that in the future to signal overlong
# vectors (like with jumbo packets)
# Though that would also mean packets larger than 64 KiB,
# which we're a long way from
return f"""\
if (!real_packet->{location}) {{
/* Transmit null vector as empty vector */
e |= DIO_PUT(arraylen, &dout, &field_addr, 0);
}} else {{
int {location.index};
fc_assert(strvec_size(real_packet->{location}) < MAX_UINT16);
e |= DIO_PUT(arraylen, &dout, &field_addr, strvec_size(real_packet->{location}));
#ifdef FREECIV_JSON_CONNECTION
{location.json_subloc} = plocation_elem_new(0);
#endif /* FREECIV_JSON_CONNECTION */
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
const char *pstr = strvec_get(real_packet->{location}, {location.index});
if (!pstr) {{
/* Transmit null strings as empty strings */
pstr = "";
}}
#ifdef FREECIV_JSON_CONNECTION
/* Next array element */
{location.json_subloc}->number = {location.index};
#endif /* FREECIV_JSON_CONNECTION */
e |= DIO_PUT({self.dataio_type}, &dout, &field_addr, pstr);
}}
#ifdef FREECIV_JSON_CONNECTION
FC_FREE({location.json_subloc});
#endif /* FREECIV_JSON_CONNECTION */
}}
"""
def get_code_put(self, location: Location, deep_diff: bool = False) -> str:
assert not deep_diff, "deep-diff for strvec not supported yet"
return self._get_code_put_full(location)
def _get_code_get_full(self, location: Location) -> str:
return f"""\
{{
int {location.index};
if (!DIO_GET(arraylen, &din, &field_addr, &{location.index})) {{
RECEIVE_PACKET_FIELD_ERROR({location.name});
}}
strvec_reserve(real_packet->{location}, {location.index});
#ifdef FREECIV_JSON_CONNECTION
{location.json_subloc} = plocation_elem_new(0);
#endif /* FREECIV_JSON_CONNECTION */
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
char readin[MAX_LEN_PACKET];
#ifdef FREECIV_JSON_CONNECTION
/* Next array element */
{location.json_subloc}->number = {location.index};
#endif /* FREECIV_JSON_CONNECTION */
if (!DIO_GET({self.dataio_type}, &din, &field_addr, readin, sizeof(readin))
|| !strvec_set(real_packet->{location}, {location.index}, readin)) {{
RECEIVE_PACKET_FIELD_ERROR({location.name});
}}
}}
#ifdef FREECIV_JSON_CONNECTION
FC_FREE({location.json_subloc});
#endif /* FREECIV_JSON_CONNECTION */
}}
"""
def get_code_get(self, location: Location, deep_diff: bool = False) -> str:
assert not deep_diff, "deep-diff for strvec not supported yet"
return self._get_code_get_full(location)
def __str__(self) -> str:
return f"{self.dataio_type}({self.public_type})"
def string_type_ctor(dataio_type: str, public_type: str) -> RawFieldType:
"""Field type constructor for both strings and string vectors"""
if dataio_type not in ("string", "estring"):
raise ValueError(f"not a valid string type: {dataio_type}")
if public_type == "char":
return NeedSizeType(dataio_type, public_type, cls = StringType)
elif public_type == "struct strvec":
return StrvecType(dataio_type, public_type)
else:
raise ValueError(f"public type {public_type} not legal for dataio type {dataio_type}")
DEFAULT_REGISTRY.dataio_types["string"] = DEFAULT_REGISTRY.dataio_types["estring"] = string_type_ctor
class Field:
"""A single field of a packet. Consists of a name, type information
(including array sizes) and flags."""
......
def get_init(self) -> str:
"""Generate code initializing this field in the packet struct, after
the struct has already been zeroed."""
return self.type_info.get_code_init(Location(self.name))
return self.type_info.get_code_init(Location(self.name, f"packet->{self.name}"))
def get_copy(self, dest: str, src: str) -> str:
"""Generate code deep-copying this field from *src to *dest."""
......
def get_free(self) -> str:
"""Generate code deinitializing this field in the packet struct
before destroying the packet."""
return self.type_info.get_code_free(Location(self.name))
return self.type_info.get_code_free(Location(self.name, f"packet->{self.name}"))
def get_hash(self) -> str:
"""Generate code factoring this field into a hash computation."""
common/networking/packets.def
type REQUIREMENT = requirement(struct requirement)
type ACT_PROB = action_probability(struct act_prob)
type STRING = string(char)
# A string vector encoded to a string outside the packet and field system.
# Marking it this way is useful as documentation. The marking can also be
# used in non vanilla generate_packets.py packet generators.
type STRVEC = STRING
type STRVEC = string(struct strvec)
type WORKLIST = worklist(struct worklist)
# string that is URI encoded in the JSON protocol
type ESTRING = estring(char)
......
VLAYER vlayer;
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
BV_UTYPE_FLAGS flags;
BV_UTYPE_ROLES roles;
......
UINT8 reqs_count;
REQUIREMENT reqs[MAX_NUM_REQS:reqs_count];
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_GOVERNMENT_RULER_TITLE = 143; sc, lsend
......
UINT32 num_reqs;
STRING name[MAX_LEN_NAME];
STRING rule_name[MAX_LEN_NAME];
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
STRING graphic_str[MAX_LEN_NAME];
STRING graphic_alt[MAX_LEN_NAME];
end
......
STRING rule_name[MAX_LEN_NAME];
STRING graphic_str[MAX_LEN_NAME];
STRING graphic_alt[MAX_LEN_NAME];
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_TERRAIN_CONTROL = 146; sc, lsend
......
STRING soundtag[MAX_LEN_NAME];
STRING soundtag_alt[MAX_LEN_NAME];
STRING soundtag_alt2[MAX_LEN_NAME];
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_IMPR_FLAG = 20; sc, lsend
......
UINT8 color_green;
UINT8 color_blue;
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_TERRAIN_FLAG = 231; sc, lsend
......
UINT16 non_native_def_pct;
BV_UCLASS_FLAGS flags;
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_EXTRA = 232; sc, lsend
......
BV_EXTRAS bridged_over;
BV_EXTRAS conflicts;
SINT8 no_aggr_near_city;
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_EXTRA_FLAG = 226; sc, lsend
......
UINT16 to_pct;
UINT16 onetime_pct;
BV_GOODS_FLAGS flags;
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_DISASTER = 224; sc, lsend
......
PACKET_RULESET_COUNTER = 513; sc, lsend
STRING name[MAX_LEN_NAME];
STRING rule_name[MAX_LEN_NAME];
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
UINT32 def;
UINT32 checkpoint;
COUNTER_TARGET type;
......
STRING rule_name[MAX_LEN_NAME];
UINT8 reqs_count;
REQUIREMENT reqs[MAX_NUM_REQS:reqs_count];
STRVEC helptext[MAX_LEN_PACKET];
STRVEC helptext;
end
PACKET_RULESET_CLAUSE = 512; sc, lsend
common/networking/packets.h
enum packet_type packet_type);
bool packet_check(struct data_in *din, struct connection *pc);
/* Utilities to exchange strings and string vectors. */
#define PACKET_STRVEC_SEPARATOR '\3'
#define PACKET_STRVEC_COMPUTE(str, strvec) \
if (NULL != strvec) { \
strvec_to_str(strvec, PACKET_STRVEC_SEPARATOR, str, sizeof(str)); \
} else { \
str[0] = '\0'; \
}
#define PACKET_STRVEC_EXTRACT(strvec, str) \
if ('\0' != str[0]) { \
strvec = strvec_new(); \
strvec_from_str(strvec, PACKET_STRVEC_SEPARATOR, str); \
} else { \
strvec = NULL; \
/* Utilities to move string vectors in and out of packets. */
#define PACKET_STRVEC_INSERT(dest, src) \
dest = src
#define PACKET_STRVEC_EXTRACT(dest, src) \
if (src != nullptr && strvec_size(src) > 0) { \
dest = strvec_new(); \
strvec_copy(dest, src); \
} else { \
dest = nullptr; \
}
#ifdef __cplusplus
common/networking/packets_json.h
FREE_PACKET_STRUCT(&packet_buf); \
return NULL;
/* Utilities to exchange strings and string vectors. */
#define PACKET_STRVEC_SEPARATOR '\3'
#define PACKET_STRVEC_COMPUTE(str, strvec) \
if (NULL != strvec) { \
strvec_to_str(strvec, PACKET_STRVEC_SEPARATOR, str, sizeof(str)); \
} else { \
str[0] = '\0'; \
}
#define PACKET_STRVEC_EXTRACT(strvec, str) \
if ('\0' != str[0]) { \
strvec = strvec_new(); \
strvec_from_str(strvec, PACKET_STRVEC_SEPARATOR, str); \
} else { \
strvec = NULL; \
/* Utilities to move string vectors in and out of packets. */
#define PACKET_STRVEC_INSERT(dest, src) \
dest = src
#define PACKET_STRVEC_EXTRACT(dest, src) \
if (src != nullptr && strvec_size(src) > 0) { \
dest = strvec_new(); \
strvec_copy(dest, src); \
} else { \
dest = nullptr; \
}
#ifdef __cplusplus
fc_version
# On FREECIV_DEBUG builds, optional capability "debug" gets automatically
# appended to this.
#
NETWORK_CAPSTRING="+Freeciv.Devel-${MAIN_VERSION}-2024.Apr.17"
NETWORK_CAPSTRING="+Freeciv.Devel-${MAIN_VERSION}-2024.Apr.20"
FREECIV_DISTRIBUTOR=""
if test "x$FREECIV_LABEL_FORCE" != "x" ; then
server/ruleset/ruleload.c
packet.non_native_def_pct = c->non_native_def_pct;
packet.flags = c->flags;
PACKET_STRVEC_COMPUTE(packet.helptext, c->helptext);
PACKET_STRVEC_INSERT(packet.helptext, c->helptext);
lsend_packet_ruleset_unit_class(dest, &packet);
} unit_class_iterate_end;
......
packet.work_raise_chance[i] = vlevel->work_raise_chance;
}
}
PACKET_STRVEC_COMPUTE(packet.helptext, u->helptext);
PACKET_STRVEC_INSERT(packet.helptext, u->helptext);
packet.worker = u->adv.worker;
......
} requirement_vector_iterate_end;
packet.reqs_count = j;
PACKET_STRVEC_COMPUTE(packet.helptext, s->helptext);
PACKET_STRVEC_INSERT(packet.helptext, s->helptext);
lsend_packet_ruleset_specialist(dest, &packet);
} specialist_type_iterate_end;
......
packet.flags = a->flags;
packet.cost = a->cost;
packet.num_reqs = a->num_reqs;
PACKET_STRVEC_COMPUTE(packet.helptext, a->helptext);
PACKET_STRVEC_INSERT(packet.helptext, a->helptext);
lsend_packet_ruleset_tech(dest, &packet);
} advance_iterate_end;
......
packet.type = pcount->target;
packet.def = pcount->def;
PACKET_STRVEC_COMPUTE(packet.helptext, pcount->helptext);
PACKET_STRVEC_INSERT(packet.helptext, pcount->helptext);
lsend_packet_ruleset_counter(dest, &packet);
} city_counters_iterate_end;
}
......
sz_strlcpy(packet.soundtag, b->soundtag);
sz_strlcpy(packet.soundtag_alt, b->soundtag_alt);
sz_strlcpy(packet.soundtag_alt2, b->soundtag_alt2);
PACKET_STRVEC_COMPUTE(packet.helptext, b->helptext);
PACKET_STRVEC_INSERT(packet.helptext, b->helptext);
lsend_packet_ruleset_building(dest, &packet);
} improvement_iterate_end;
......
packet.color_green = pterrain->rgb->g;
packet.color_blue = pterrain->rgb->b;
PACKET_STRVEC_COMPUTE(packet.helptext, pterrain->helptext);
PACKET_STRVEC_INSERT(packet.helptext, pterrain->helptext);
lsend_packet_ruleset_terrain(dest, &packet);
} terrain_type_iterate_end;
......
packet.bridged_over = e->bridged_over;
packet.conflicts = e->conflicts;
PACKET_STRVEC_COMPUTE(packet.helptext, e->helptext);
PACKET_STRVEC_INSERT(packet.helptext, e->helptext);
lsend_packet_ruleset_extra(dest, &packet);
} extra_type_iterate_end;
......
packet.onetime_pct = g->onetime_pct;
packet.flags = g->flags;
PACKET_STRVEC_COMPUTE(packet.helptext, g->helptext);
PACKET_STRVEC_INSERT(packet.helptext, g->helptext);
lsend_packet_ruleset_goods(dest, &packet);
} goods_type_iterate_end;
......
sz_strlcpy(gov.rule_name, rule_name_get(&g->name));
sz_strlcpy(gov.graphic_str, g->graphic_str);
sz_strlcpy(gov.graphic_alt, g->graphic_alt);
PACKET_STRVEC_COMPUTE(gov.helptext, g->helptext);
PACKET_STRVEC_INSERT(gov.helptext, g->helptext);
lsend_packet_ruleset_government(dest, &gov);
......
} requirement_vector_iterate_end;
packet.reqs_count = j;
PACKET_STRVEC_COMPUTE(packet.helptext, pmul->helptext);
PACKET_STRVEC_INSERT(packet.helptext, pmul->helptext);
lsend_packet_ruleset_multiplier(dest, &packet);
} multipliers_iterate_end;
(1-1/2)