Skip to content

Fix each Property's position when the Engine is created, and read every Property by position - #137

Merged
jwrosewell merged 12 commits into
rcfrom
doc/properties-by-position-in-c
Nov 11, 2026
Merged

jwrosewell merged 12 commits into
rcfrom
doc/properties-by-position-in-c

Conversation

@jwrosewell

@jwrosewell jwrosewell commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

device-detection-on-premise.md and ip-intelligence-on-premise.md, under Finding Properties on rc at 67ead69, let an Engine be created "with none to have every Property of the data file". They say such an Engine has no positions, so an implementation "MUST read the Properties of such an Engine by name". They also let an implementation read an Engine created with names "by its position ... or by its name".

That leaves two ways to read a Property. A read by name looks the name up in the native code on every read. An Engine with no names had no position that stayed the same across a data refresh, because C gave each Property its place in the data set loaded, sorted by name, and a refresh to a file with other Properties moved those places.

The decisions

James Rosewell, 10 November 2026:

The change for the index of the property is to return the index of the property in the list of properties provided at BUILD time and not for the data set. This is because when a reload happens other properties might be in different orders. The use of the index to avoid the string look up should be applied everywhere.

At f8f7482 this draft met that rule by having a language configured with no names pass every Property name of the data set when it created the Engine. Measured in the Go, Rust, PHP and Python libraries, that made processing 3 to 7 times slower, because an Engine created with names walks every component's graph during process. Later on 10 November James decided that C changes instead, so that an Engine created with no names keeps the first load's names, in their order, across every reload, and processing stays as fast as today. That C change is 51degrees-cxx#148, and this draft now describes it.

What changed

  1. Finding Properties, both pages.
    • The opening paragraph again says an Engine can be created with no names, now "to have every Property of the first data file it loads". The position of a Property is still the index of its name in the names the Engine was created with, fixed when the Engine is created.
    • An Engine created with no names takes the names of every Property of the first data file it loads, sorted by name, which is the order of its list of available properties (properties.c lines 473 to 484 sort them, and properties.h lines 40 to 42 define that list as in "ascending order of name"), keeps them across every data refresh, and so has fixed positions too. The native code does this. An implementation configured with no names MUST create the Engine with no names and MUST NOT pass the data set's names itself, because a named Engine walks graphs during process, where one with no names walks a graph only at the first read of one of its Properties.
    • A Property only a later data file has gets no position until the Engine is created again, so a read of it, by position or by name, gives no value with the invalid Property reason. Its Property metadata reports it as not available, with Available false, because cxx#148 at 9871e355 makes the metadata's availability ask for the required property index (hash.c line 6124 and ipi.c line 4257), and every language exposes that flag. With Available false, step 3.1 of Deciding the reason gives PropertyExcludedFromEngineConfiguration. Both pages now say that an Engine created with no names has the names of its first data file as its configuration, so such a Property counts as excluded from that configuration and building the Engine again includes it. No new reason is added, because every language already has this one.
    • Every read MUST use the position. A typed accessor resolves its position once. A read by name goes through a table from the names the Engine was created with to their positions. The table is built once, when the Engine is created, from the names the native code gives (for example through the Engine's getRequiredProperties), and MUST NOT be built again from a later data set.
    • The native code gives each position. The page quotes the C comment on the Required Property Index at cxx#148's head (below), and links the reload code that copies the names and works out each index again.
    • The C++ scoping stays. Read through the C++ ResultsHash or ResultsIpi class, the methods that take a position read by it whether or not the Engine was created with names, getPropertyName gives the name at a position, and the methods that take a name find its position first. The bullet that said such an Engine "has no positions at all", with its link to allowedRequiredPropertyIndex, is gone, because cxx#148 removes that method.
    • The bullet that had a language pass the data set's names, and the reading of "Without names, each property's index is its own place" that justified it, are gone.
    • The order of getProperties now "need not be" the order of the positions, rather than "is not", because for an Engine with no names on its first data file the two orders are the same. After a data refresh it can list a Property only the new data file has, which has no position and reads as invalid, as cxx#148 documents (properties.h lines 76 to 81 and point 2 of its body).
  2. Reading graphs, both pages. An Engine created with no names walks no graph in process "before or after a data refresh", which cxx#148 keeps (fiftyoneDegreesResultsWalkWhenPopulated in src/common/results.h).
  3. cloud-request-engine.md line 190 linked json-builder.md#processing, but the heading in json-builder.md is ## Process, so the link is now #process. No other page links json-builder.md#processing.

The performance guidance on both pages ("Positions need no such store, as each is the index of a Property in the names the Engine was created with") and the IP page's copyValueAsUTF8String bullet are as on rc, as both are true under the rule.

Checked in the native code

At 51degrees-cxx 1a92d51e, the commit that merged cxx#148 into the cxx rc. Every linked line range is the same there as at 9a535a49, the head when these links were first made.

  • The comment on the Required Property Index (src/common/properties.h lines 31 to 38) reads as follows, and both pages quote it.

    Required Property Index : the 0 based index of the property in the names the caller gave when the data set was created, which is the index every read by position takes. A reload does not change it. A name the data does not have keeps its index, and reads as a property the data set does not have. When the caller named no property, the names are those of the first data set loaded, in the order of its list of available properties, and a reload keeps them, so a property that only a later data set holds gets no index.

  • fiftyoneDegreesPropertiesCreate (src/common/properties.c lines 553 to 572) carries the comment "Existing properties that name nothing give the names of their data set, in its order, so a reload keeps each index on its property", and copies those names with copyNamesFromAvailable. setRequiredPropertyIndexes (lines 508 to 536) then finds each name in the new data set and gives it index i.

  • ResultsBase::getValues(int) passes its index straight on (src/common/ResultsBase.cpp lines 436 to 439), as do the other reads by index, and allowedRequiredPropertyIndex is gone. getPropertyName(int) gives the name at an index (lines 85 to 95), and getProperties still lists the data set's Properties in the order of its list of available properties (lines 75 to 83).

  • cxx#148's body gives process times for an Engine with no names before and after the change, and finds it no slower. Its tests reload from the Lite file to the TAC file, and from the ASN file to the Enterprise file, and check that every index is kept.

Checked here

Outstanding

  • Review of the wording.
  • 51degrees-cxx#148, the C behaviour both pages describe, merged into the cxx rc as 1a92d51e, and the links in both pages and here point at that commit.
  • Every language takes cxx#148 with its next move of the native code. It then reads by position on an Engine with no names without passing the names, and builds its table for reads by name once from the names the native code gives.

No version directive is carried.

…e C++ Results classes

In C, each property's index is its own place when no names are given (src/common/properties.c lines 479 to 491 in 51degrees-cxx at cfad8978), so an implementation that calls C may read such an Engine by position. The C++ ResultsHash and ResultsIpi classes refuse every position without names (ResultsBase.cpp lines 481 to 484), so the MUST read by name stays for them. The native code still gives each position. Also point the Cloud Request Engine's JSON Builder link at #process, the heading json-builder.md has.
C resolves a position against the data set the results were populated from (51degrees-cxx cfad8978 src/dd/hash/hash.c lines 5896 to 5901, src/ipi/ipi.c line 3342), so a position taken from the current data set after a refresh can name a different Property.
James Rosewell decided on 10 November that a property's index is its place in the list of names the engine was built with, fixed at build. An engine configured with no names takes every property name in the data set at build as that list, every read uses the index, and a read by name uses a table built once from the build-time names, never from the data set. This replaces the earlier text that let C read an engine with no names by its data set position. The C quote, the C++ scoping and the json-builder anchor fix stay.
@jwrosewell jwrosewell changed the title Scope the read-by-name rule for an Engine created with no names to the C++ Results classes Fix each Property's position when the Engine is created, and read every Property by position Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant