mirror of
https://gitlab.com/fabinfra/fabaccess/fabaccess-api.git
synced 2025-03-13 07:11:50 +01:00
More changes because breaking existing code is fun
This commit is contained in:
parent
c185283f35
commit
170d4df51c
@ -1,28 +0,0 @@
|
|||||||
@0xff5b4a767d98592a;
|
|
||||||
|
|
||||||
using Rust = import "programming_language/rust.capnp";
|
|
||||||
$Rust.parentModule("schema");
|
|
||||||
|
|
||||||
using CSharp = import "programming_language/csharp.capnp";
|
|
||||||
$CSharp.namespace("FabAccessAPI.Schema");
|
|
||||||
|
|
||||||
struct UUID {
|
|
||||||
# UUID type used to identify machines.
|
|
||||||
# Since the exact value has no meaning the encoding rules are not too relevant, but it is
|
|
||||||
# paramount that you are consistent when encoding and decoding this type.
|
|
||||||
#
|
|
||||||
# Consider using this algorithm for assembling the 128-bit integer:
|
|
||||||
# (assuming ISO9899:2018 shifting & casting rules)
|
|
||||||
# uint128_t num = (uuid1 << 64) + uuid0;
|
|
||||||
# And then respectively this code for deconstructing it:
|
|
||||||
# uint64_t uuid0 = (uint64_t) num;
|
|
||||||
# uint64_t uuid1 = (uint64_t) (num >> 64);
|
|
||||||
|
|
||||||
uuid0 @0 :UInt64;
|
|
||||||
uuid1 @1 :UInt64;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct KeyValuePair {
|
|
||||||
key @0 :Text;
|
|
||||||
value @1 :Text;
|
|
||||||
}
|
|
247
resource.capnp
247
resource.capnp
@ -9,129 +9,188 @@ $CSharp.namespace("FabAccessAPI.Schema");
|
|||||||
using Persistent = import "/capnp/persistent.capnp".Persistent;
|
using Persistent = import "/capnp/persistent.capnp".Persistent;
|
||||||
using Value = import "/capnp/schema.capnp".Value;
|
using Value = import "/capnp/schema.capnp".Value;
|
||||||
|
|
||||||
using General = import "general.capnp";
|
|
||||||
using User = import "user.capnp".User;
|
using User = import "user.capnp".User;
|
||||||
using Space = import "space.capnp".Space;
|
using L10NString = import "utils.capnp".L10NString;
|
||||||
|
using UUID = import "utils.capnp".UUID;
|
||||||
|
|
||||||
struct Node {
|
struct Resource {
|
||||||
# A node in the state tree. If it's the root note this struct "contains" the whole tree.
|
# BFFH's smallest unit of a physical or abstract "thing".
|
||||||
|
# A resource can be as simple and physical as a table, as complex as a PCB production line or as
|
||||||
|
# abstract as "people with specific know-how are present".
|
||||||
|
|
||||||
# TODO: I'm not happy with this representation. While it's about as generic as we can get it's
|
uuid @0 :UUID;
|
||||||
# unhandly because all clients and servers have to always manually check every leaf of the
|
# An stable, globally unique descriptor for a resource. Two resources with the same UUID are
|
||||||
# state tree, relying on convention instead of static type checking. But I'm not sure how else
|
# (almost¹) guaranteed to be the same instance, and the UUID of a resource will survive through
|
||||||
# to represent the state extensibly in a way that lets us evolve the protocol by stabilizing
|
# server restarts, renaming, reconfiguration etc.
|
||||||
# extensions. One option could be to use OID or UUID as "tag bits" and "stabilize" them by
|
#
|
||||||
# defining those as `const` values, but that wouldn't give us proper type checking either.
|
# [¹]: UUID are 128-bit integer. A collision is *possible*, just *very* unlikely. If you
|
||||||
|
# generate 1 billion UUID every second for the next 100 years you have a chance for a collision
|
||||||
|
# of about 50%.
|
||||||
|
|
||||||
part @0 :Text;
|
|
||||||
# Name of the node, making up a path to this node (e.g. "set/colour/red")
|
|
||||||
|
|
||||||
union {
|
id @1 :Text;
|
||||||
# Content of a node. A node has either children *or* a value, not both.
|
# Every resource in BFFH has a human-readable "name" that is locally unique, but not persistent.
|
||||||
|
# That is a resource called "hello" today may be called "bye" tomorrow and a resource called
|
||||||
|
# "hi~~" may not be the same resource as the resource called "hi~~" yesterday. This name is
|
||||||
|
# canonical and thus identifying. There is exactly *one* valid representation of this name at
|
||||||
|
# any given point in time. Thus this name can also not be translated.
|
||||||
|
|
||||||
children @1 :List(Node);
|
name @2 :L10NString;
|
||||||
# Node is not a leaf node ⇒ it has a list of children
|
# A resource may also have a human-meaningful name that is designed to be shown to users. This
|
||||||
|
# name does not have to be unique or identifiable or canonical, its main use is to be
|
||||||
|
# human-meaningful. For example a "name" could be the translations:
|
||||||
|
# - (en, "Prusa SL1 SLA-Printer")
|
||||||
|
# - (de, "Prusa SL1 SLA-Drucker")
|
||||||
|
# - (es, "Impresora 3D de SLA Prusa SL1")
|
||||||
|
|
||||||
value @2 :Value;
|
description @3 :L10NString;
|
||||||
# Node is a leaf node ⇒ it contains a (typed) Cap'n Proto value.
|
# A resource may have a description attached to tell an user some more information on a resource
|
||||||
# The type `Value` comes from the Cap'n Proto schema definition file (usually
|
# in a free-form format.
|
||||||
# /usr/include/capnp/schema.capnp) and can be any basic capnp type, including lists and
|
# Similar to the human-meaningful name this description can be translated.
|
||||||
# structs (as :AnyPointer which a client has to cast)
|
|
||||||
}
|
notify @4 :Notify;
|
||||||
|
# Readonly access to the state of a resource.
|
||||||
|
# A resource can have "state". State are values attached to a resource that describe a specific
|
||||||
|
# state that users or administrators want this resource to be in. Usually this state consists of
|
||||||
|
# a number of primitive values encoding for example "turned on" or "turned off".
|
||||||
|
# Users with the required permissions may change this state.
|
||||||
|
|
||||||
|
claimable @5 :Claimable;
|
||||||
|
# Writeable access to the state of a resource.
|
||||||
|
# Resources are semaphores. They allow writeable access for n ∈ ℕ\{0} clients, depending on the
|
||||||
|
# exact resource in question. In some cases n ≔ 1, and the only write access is exclusive.
|
||||||
|
# "Claims" model this by requiring a client to first assert a claim, thus reserving a semaphore
|
||||||
|
# slot or failing if no more are available, and then using this claim to write to a resources
|
||||||
|
# state.
|
||||||
|
|
||||||
|
interestable @6 :Interestable;
|
||||||
|
# Sometimes clients are not just interested in the state of a resource but rather want a
|
||||||
|
# resource to stay in a specific state. e.g. somebody working in a makerspace wants the space to
|
||||||
|
# stay open, even though they themselves may not have permission to keep the makerspace open.
|
||||||
|
# "Interest" represents this. Specifically right now it tells BFFH that the client wants at
|
||||||
|
# least one `Claim` to remain.
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Applied {
|
struct Map(Key, Value) {
|
||||||
# Encodes if a specific actor has applied/verified a state change
|
# A serialized key-value map represented as a list of (k,v) tuples.
|
||||||
|
|
||||||
name @0 :Text;
|
entries @0 : List(Entry);
|
||||||
# Name of the actor
|
|
||||||
|
|
||||||
state @1 :State;
|
struct Entry {
|
||||||
# State of the state change in the actor
|
key @0 :Key;
|
||||||
enum State {
|
val @1 :Value;
|
||||||
unapplied @0;
|
|
||||||
applied @1;
|
|
||||||
verified @2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
using State = Map(Text, Value);
|
||||||
|
# Update state provided to a resource via a claim is represented as a Map of human-readable
|
||||||
|
# identifiers to Cap'n Proto Values. These Values can be either primitive types such as Uint8,
|
||||||
|
# Float64 or more complex types such as structs, lists, or enums.
|
||||||
|
# The resulting state of a resource, which is the output of whatever internal logic the resource
|
||||||
|
# implements, is also represented in this form, but the keys and also values may be different.
|
||||||
|
#
|
||||||
|
# Later on very common cases (use, register, return, etc.) can get shortcut functions in the Claim
|
||||||
|
# interface that pre-emptively check permissions and ability (so you get the respective cap iff the
|
||||||
|
# resource supports that update and if you're allowed to do that) but these functions only serve to
|
||||||
|
# make the update more efficient than calling `update` with the string identifier and dynamic typed
|
||||||
|
# value but do the exact same serverside as an `update` call would. This way we can make future
|
||||||
|
# versions of the API more efficient and easier to use while not breaking compatibility with old
|
||||||
|
# clients.
|
||||||
|
#
|
||||||
|
# TODO: This has the potential problem that a newer client can not distinguish between a server
|
||||||
|
# using an old version of the API and a client simply not being allowed to call a specific shortcut
|
||||||
|
# method because in both cases that cap will be a nullptr. Could be solved by making `Claim` a
|
||||||
|
# struct and indicating which shortcut methods it knows of.
|
||||||
|
# Not sure if this is a big problem, we optimize for old clients and up-to-date servers.
|
||||||
|
#
|
||||||
|
# TODO: We should provide a number of sensible implementations for common complex `Value` types such
|
||||||
|
# as "colour", "temperature", etc. and define identifiers for common values.
|
||||||
|
|
||||||
interface Access {
|
interface Access {
|
||||||
# Allow syncronous read access to a resource's state. You're not given this capability directly
|
# Allow syncronous read access to a resource's output state. You're not given this capability
|
||||||
# but instead Notify, Interest and Claim all extend it, allowing you to call these methods from
|
# directly but instead Notify, Interest and Claim all extend it, allowing you to call these
|
||||||
# any of those.
|
# methods on any of those.
|
||||||
|
|
||||||
readState @0 Node;
|
readOutput @0 State;
|
||||||
readApplied @1 Applied;
|
|
||||||
|
|
||||||
# TODO: There should probably be a more efficient approach for reading state than "read *all*
|
# TODO: There should probably be a more efficient approach for reading state than "read *all*
|
||||||
# state".
|
# state".
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Notify extends(Access) {
|
interface Notify extends(Access) {
|
||||||
# The Notify interface allows clients to be informed about state changes asyncronously.
|
# The Notify interface allows clients to be informed about state changes asyncronously. It is
|
||||||
# It is mainly designed around the `register` function which allows a client to register a
|
# mainly designed around the `register` function which allows a client to register a callback
|
||||||
# Callback on the client that is called every time state changes happen to the resource in
|
# that is called every time state changes happen to the resource in question.
|
||||||
# question.
|
#
|
||||||
# Notify are ephermal. If the connection to the server is lost all `Notify` are unregistered
|
# Notify are ephermal. If the connection to the server is lost all `Notify` from that client are
|
||||||
|
# unregistered.
|
||||||
|
|
||||||
register @0 ( cb: Callback );
|
register @0 ( cb: Callback );
|
||||||
# Register a given callback to be called on every state update. If this client already has a
|
# Register a given callback to be called on every state update. If this client already has a
|
||||||
# callback registered for this resource the old callback is replaced.
|
# callback registered for this resource the old callback is replaced.
|
||||||
# The two fields `state` and `applied` indicate interest for `state` and `applied`. If they are
|
|
||||||
# unset the respective method on the callback will not be called.
|
|
||||||
|
|
||||||
unregister @1 ();
|
unregister @1 ();
|
||||||
# Unregister the current callback, if any.
|
# Unregister this callback
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
# This callback interface is implemented on the client
|
# This callback interface needs to be implemented on the client
|
||||||
|
|
||||||
newState @0 Node;
|
newState @0 State;
|
||||||
# A server will call newState() with the updated set state tree if `state` was set to `true`
|
# A server will call newState() with the updated output state. However a server will only
|
||||||
# in `register`, however unless the last call to newState() didn't complete yet, as to not
|
# allow one in-flight call, so as long as the previous call to newState() hasn't completed
|
||||||
# overload a client.
|
# the server will drop intermediary updates as to not overload a client.
|
||||||
|
# Specifically, example timeline:
|
||||||
|
# 1. Update A
|
||||||
|
# 2. Server calls newState(A)
|
||||||
|
# 3. Update B
|
||||||
|
# 4. Update C
|
||||||
|
# 5. Call to newState(A) completes
|
||||||
|
# 6. Server calls newState(C)
|
||||||
|
# So Update B was never sent to the client but the client will eventually always end up with
|
||||||
|
# the latest state.
|
||||||
|
|
||||||
# TODO: There should probably be a more efficient approach here too, something along the
|
# TODO: There should probably be a more efficient approach here too, something along the
|
||||||
# lines of server-side filtering.
|
# lines of server-side filtering.
|
||||||
|
|
||||||
# TODO: Add newApplied?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Interestable {
|
interface Interestable {
|
||||||
interest @0 () -> ( interest: Interest );
|
# "Interest" right now it tells BFFH that the client wants at least one `Claim` to remain.
|
||||||
}
|
|
||||||
|
|
||||||
interface Interest extends(Access) {
|
register @0 ( cb: Callback ) -> ( handle :Handle );
|
||||||
register @0 ( cb: Callback );
|
# Register a callback that BFFH will use to send notifications back to the client asyncronously.
|
||||||
unregister @1 ();
|
# This creates an "Interest" on this resource. Setting the callback to a `nullptr` will still
|
||||||
|
# register an interest but the server will not be able to inform a client about an impeding
|
||||||
|
# claim drop.
|
||||||
|
|
||||||
blocking @2 ();
|
blocking @1 ();
|
||||||
# As an alternative to the `register`/`Callback` system you can also call `blocking` which will
|
# As an alternative to the `register`/`Callback` system you can also call `blocking` which will
|
||||||
# — as the name suggests — block until the last claim was dropped.
|
# — as the name suggests — block until the last claim is being dropped. This will register an
|
||||||
|
# ephermal Interest that can not survive a disconnect.
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
drop @0 ();
|
drop @0 ();
|
||||||
# The last claim on the resource this Interest is registered on was dropped, invalidating
|
# The last claim on the resource this Interest is registered is being dropped, invalidating
|
||||||
# the Interest.
|
# the Interest.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Handle {
|
||||||
|
# A Handle back to the server side Interest registered. Destroying this capability will also
|
||||||
|
# inform the server and remove the Interest again.
|
||||||
|
# TODO: `extends (Persistance)` so that clients can `save` this capability and thus make the
|
||||||
|
# Interest survive disconnects.
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface Claimable {
|
interface Claimable {
|
||||||
# Having this capability set (i.e. not be a `nullptr`) means the user has at least writeable
|
# Having this capability set (i.e. not be a `nullptr`) means the user has at least writeable
|
||||||
# access to a resource and the resource is claimable (n > 0).
|
# access to a resource and the resource is claimable (n > 0).
|
||||||
|
|
||||||
claim @0 () -> ClaimResponse;
|
claim @0 () -> ClaimResponse;
|
||||||
# Assert a claim on a resource.
|
# Assert a claim on a resource.
|
||||||
}
|
|
||||||
|
|
||||||
interface Lockable {
|
struct ClaimResponse {
|
||||||
# Having this capability set means the user has managerial access to a resource.
|
|
||||||
|
|
||||||
lock @0 () -> ( lock: Claim );
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ClaimResponse {
|
|
||||||
enum Error {
|
enum Error {
|
||||||
# Error describing why a claim failed.
|
# Error describing why a claim failed.
|
||||||
|
|
||||||
@ -140,11 +199,21 @@ struct ClaimResponse {
|
|||||||
|
|
||||||
locked @1;
|
locked @1;
|
||||||
# The resource was locked
|
# The resource was locked
|
||||||
|
|
||||||
|
precondition @2;
|
||||||
|
# Some precondition was not met
|
||||||
|
|
||||||
|
dependencies @3;
|
||||||
|
# Resource failed to secure dependencies
|
||||||
}
|
}
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
failed :group {
|
||||||
error @0 :Error;
|
error @0 :Error;
|
||||||
success @1 :Claim;
|
reason @1 :L10NString;
|
||||||
|
}
|
||||||
|
success @2 :Claim;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,17 +226,37 @@ interface Claim extends(Access) {
|
|||||||
# In this case the `Restorer` service could be `Claimable` / `Interestable` providing a
|
# In this case the `Restorer` service could be `Claimable` / `Interestable` providing a
|
||||||
# `restore( ref: SturdyRef )` method.
|
# `restore( ref: SturdyRef )` method.
|
||||||
|
|
||||||
update @0 Node;
|
readInput @0 () -> State;
|
||||||
|
# Get the current *input* state. This is not the output state that `Notify` or Actors get access
|
||||||
|
# to but instead the currently stored input state of a resource.
|
||||||
|
|
||||||
|
update @1 State -> UpdateResult;
|
||||||
# Update the State of the claimed resource with the given one
|
# Update the State of the claimed resource with the given one
|
||||||
}
|
|
||||||
|
|
||||||
struct Resource {
|
struct UpdateResult {
|
||||||
name @0 :Text;
|
enum Error {
|
||||||
description @1 :Text;
|
# Reason why the update failed
|
||||||
typeid @2 :Text;
|
|
||||||
|
|
||||||
notify @3 :Notify;
|
denied @0;
|
||||||
interest @4 :Interest;
|
# Update was denied beause user is missing an required permission
|
||||||
claimable @5 :Claimable;
|
|
||||||
lockable @6 :Lockable;
|
precondition @1;
|
||||||
|
# Some other precondition failed, e.g. because a required field is not set
|
||||||
|
|
||||||
|
invalid @2;
|
||||||
|
# The update is invalid, e.g. because an unknown field was set.
|
||||||
|
|
||||||
|
typeError @3;
|
||||||
|
# A field in the update has a known identifier but a bad type for that identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
union {
|
||||||
|
failed :group {
|
||||||
|
error @0 :Error;
|
||||||
|
field @1 :Text;
|
||||||
|
reason @2 :L10NString;
|
||||||
|
}
|
||||||
|
success @3 :Void;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ $Rust.parentModule("schema");
|
|||||||
using CSharp = import "programming_language/csharp.capnp";
|
using CSharp = import "programming_language/csharp.capnp";
|
||||||
$CSharp.namespace("FabAccessAPI.Schema");
|
$CSharp.namespace("FabAccessAPI.Schema");
|
||||||
|
|
||||||
using General = import "general.capnp";
|
|
||||||
using Resource = import "resource.capnp".Resource;
|
using Resource = import "resource.capnp".Resource;
|
||||||
|
|
||||||
interface Resources
|
interface Resources
|
||||||
|
16
space.capnp
16
space.capnp
@ -1,16 +0,0 @@
|
|||||||
@0xbacaff4190ac7d80;
|
|
||||||
|
|
||||||
using Rust = import "programming_language/rust.capnp";
|
|
||||||
$Rust.parentModule("schema");
|
|
||||||
|
|
||||||
using CSharp = import "programming_language/csharp.capnp";
|
|
||||||
$CSharp.namespace("FabAccessAPI.Schema");
|
|
||||||
|
|
||||||
using General = import "general.capnp";
|
|
||||||
|
|
||||||
struct Space
|
|
||||||
{
|
|
||||||
id @0 :General.UUID;
|
|
||||||
name @1 :Text;
|
|
||||||
info @2 :Text;
|
|
||||||
}
|
|
40
user.capnp
40
user.capnp
@ -6,39 +6,39 @@ $Rust.parentModule("schema");
|
|||||||
using CSharp = import "programming_language/csharp.capnp";
|
using CSharp = import "programming_language/csharp.capnp";
|
||||||
$CSharp.namespace("FabAccessAPI.Schema");
|
$CSharp.namespace("FabAccessAPI.Schema");
|
||||||
|
|
||||||
using General = import "general.capnp";
|
using UUID = import "utils.capnp".UUID;
|
||||||
using Space = import "space.capnp".Space;
|
|
||||||
using Role = import "role.capnp".Role;
|
using Role = import "role.capnp".Role;
|
||||||
|
|
||||||
struct User
|
struct User {
|
||||||
{
|
# Intergalactic lifeform that wants to use BFFH
|
||||||
id @0 :General.UUID;
|
|
||||||
username @1 :Text;
|
|
||||||
space @2 :Space;
|
|
||||||
|
|
||||||
struct UserInfoExtended
|
id @0 :UUID;
|
||||||
{
|
# The UUID of an user is a globally unique, persistent identifier for this user.
|
||||||
id @0 :General.UUID;
|
|
||||||
name @1 :Text;
|
username @1 :Text;
|
||||||
|
# username. Locally unique so identifying, but not persistent.
|
||||||
|
|
||||||
|
info @2 :Info;
|
||||||
|
interface Info $CSharp.name("InfoInterface") {
|
||||||
|
listRoles @0 () -> ( roles :List(Role) );
|
||||||
|
# lists explicit roles for this user. A session may have a number of additional, implicit,
|
||||||
|
# roles set by their choice of authentication or other context.
|
||||||
}
|
}
|
||||||
|
|
||||||
info @3 :Info;
|
passwd @3 :Passwd;
|
||||||
interface Info $CSharp.name("InfoInterface") {
|
interface Passwd {
|
||||||
getUserInfoExtended @0 () -> ( userInfoExtended :UserInfoExtended );
|
changepw @0 ( old: Text, new: Text );
|
||||||
listRoles @1 () -> ( roles :List(Role) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
manage @4 :Manage;
|
manage @4 :Manage;
|
||||||
interface Manage $CSharp.name("ManageInterface") {
|
interface Manage $CSharp.name("ManageInterface") {
|
||||||
pwd @0 ( old_pwd :Text, new_pwd :Text ) -> ();
|
addRole @0 ( role :Role );
|
||||||
|
removeRole @1 ( role :Role );
|
||||||
}
|
}
|
||||||
|
|
||||||
admin @5 :Admin;
|
admin @5 :Admin;
|
||||||
interface Admin $CSharp.name("AdminInterface") {
|
interface Admin $CSharp.name("AdminInterface") {
|
||||||
addRole @0 ( role :Role ) -> ();
|
setpw @0 ( new :Text );
|
||||||
removeRole @1 ( role :Role ) -> ();
|
|
||||||
|
|
||||||
pwd @2 ( new_pwd :Text ) -> ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cardDESFireEV2 @6 :CardDESFireEV2;
|
cardDESFireEV2 @6 :CardDESFireEV2;
|
||||||
|
@ -6,7 +6,6 @@ $Rust.parentModule("schema");
|
|||||||
using CSharp = import "programming_language/csharp.capnp";
|
using CSharp = import "programming_language/csharp.capnp";
|
||||||
$CSharp.namespace("FabAccessAPI.Schema");
|
$CSharp.namespace("FabAccessAPI.Schema");
|
||||||
|
|
||||||
using General = import "general.capnp";
|
|
||||||
using User = import "user.capnp".User;
|
using User = import "user.capnp".User;
|
||||||
|
|
||||||
interface Users
|
interface Users
|
||||||
|
17
utils.capnp
17
utils.capnp
@ -27,3 +27,20 @@ interface L10NString {
|
|||||||
available @1 () -> ( langs :List(Text) );
|
available @1 () -> ( langs :List(Text) );
|
||||||
# Returns the list of locales this content is available in.
|
# Returns the list of locales this content is available in.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UUID {
|
||||||
|
# UUID type used to identify machines.
|
||||||
|
# Since the exact value has no meaning the encoding rules are not too relevant, but it is
|
||||||
|
# paramount that you are consistent when encoding and decoding this type.
|
||||||
|
#
|
||||||
|
# Consider using this algorithm for assembling the 128-bit integer:
|
||||||
|
# (assuming ISO9899:2018 shifting & casting rules)
|
||||||
|
# uint128_t num = (uuid1 << 64) + uuid0;
|
||||||
|
# And then respectively this code for deconstructing it:
|
||||||
|
# uint64_t uuid0 = (uint64_t) num;
|
||||||
|
# uint64_t uuid1 = (uint64_t) (num >> 64);
|
||||||
|
|
||||||
|
lower @0 :UInt64;
|
||||||
|
upper @1 :UInt64;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user