1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
#include "peer-wire.h"
#include <float.h>
#include <limits.h>
#include <math.h>
#include <string.h>
_Static_assert(sizeof(double) == 8 && FLT_RADIX == 2 && DBL_MANT_DIG == 53 && DBL_MAX_EXP == 1024,
"peer v1 requires IEEE binary64");
static void put(unsigned char *at, uint64_t value, unsigned bytes) {
for (unsigned i = 0; i < bytes; ++i) at[bytes - 1 - i] = (unsigned char)(value >> (8 * i));
}
static uint64_t get(const unsigned char *at, unsigned bytes) {
uint64_t value = 0;
for (unsigned i = 0; i < bytes; ++i) value = (value << 8) | at[i];
return value;
}
static void put_double(unsigned char *at, double value) {
uint64_t bits;
memcpy(&bits, &value, 8);
put(at, bits, 8);
}
static double get_double(const unsigned char *at) {
uint64_t bits = get(at, 8);
double value;
memcpy(&value, &bits, 8);
return value;
}
static bool valid_action(const PureScalarPlan *plan, const PureAction *a, size_t bytes) {
if (a->phase == PURE_FRAME || a->phase == PURE_RESET)
return bytes == 12 && a->input == UINT32_MAX && a->target == UINT32_MAX;
if (a->input >= plan->input_count || a->input >= 8) return false;
PureScalarInputKind kind = plan->inputs[a->input].kind;
if (kind == PURE_KEY)
return bytes == 12 && (a->phase == PURE_DOWN || a->phase == PURE_UP) && a->target == UINT32_MAX;
if (kind == PURE_VALUE)
return bytes == 20 && a->phase == PURE_CHANGE && a->target == UINT32_MAX && isfinite(a->x);
if (kind != PURE_POINT && kind != PURE_ROUTED) return false;
if (bytes != 28 || a->phase > PURE_CANCEL || !isfinite(a->x) || !isfinite(a->y)) return false;
if (kind == PURE_POINT) return a->target == UINT32_MAX;
return a->target < plan->target_count && a->target < 24 &&
plan->targets[a->target].input == a->input;
}
void pure_peer_hello(const PureNativeApp *app, unsigned char out[PURE_PEER_HELLO_BYTES]) {
memcpy(out, "PUR1", 4);
memcpy(out + 4, app->plan->identity, 32);
out[36] = (unsigned char)app->plan->state_count;
put(out + 37, (uint64_t)app->revision, 8);
put(out + 45, (uint64_t)app->stored_revision, 8);
}
bool pure_peer_check_hello(const unsigned char *wire, size_t length, const PureScalarPlan *plan,
int64_t *revision, int64_t *stored_revision) {
if (!wire || !plan || !revision || !stored_revision || length != PURE_PEER_HELLO_BYTES ||
memcmp(wire, "PUR1", 4) || memcmp(wire + 4, plan->identity, 32) ||
wire[36] != plan->state_count) return false;
uint64_t local = get(wire + 37, 8), stored = get(wire + 45, 8);
if (local > INT64_MAX || stored > INT64_MAX) return false;
*revision = (int64_t)local; *stored_revision = (int64_t)stored;
return true;
}
size_t pure_peer_encode_request(const PurePeerRequest *request, const PureScalarPlan *plan,
unsigned char out[PURE_PEER_REQUEST_MAX]) {
if (!request || !plan || !out || request->operation > PURE_PEER_LOAD) return 0;
out[0] = (unsigned char)request->operation;
if (request->operation == PURE_PEER_STATE) return 1;
if (request->expected < 0) return 0;
put(out + 1, (uint64_t)request->expected, 8);
if (request->operation != PURE_PEER_ACTION) return 9;
const PureAction *a = &request->action;
size_t length = 12;
if (a->phase != PURE_FRAME && a->phase != PURE_RESET && a->input < plan->input_count) {
PureScalarInputKind kind = plan->inputs[a->input].kind;
if (kind == PURE_VALUE) length = 20;
if (kind == PURE_POINT || kind == PURE_ROUTED) length = 28;
}
if (!valid_action(plan, a, length)) return 0;
out[9] = a->input == UINT32_MAX ? 255 : (unsigned char)a->input;
out[10] = (unsigned char)a->phase;
out[11] = a->target == UINT32_MAX ? 255 : (unsigned char)a->target;
if (length >= 20) put_double(out + 12, a->x);
if (length == 28) put_double(out + 20, a->y);
return length;
}
bool pure_peer_decode_request(const unsigned char *wire, size_t length, const PureScalarPlan *plan,
PurePeerRequest *out) {
if (!wire || !plan || !out || !length || wire[0] > PURE_PEER_LOAD) return false;
PurePeerRequest candidate = {.operation = (PurePeerOperation)wire[0]};
if (candidate.operation == PURE_PEER_STATE) {
if (length != 1) return false;
} else {
if (length < 9 || get(wire + 1, 8) > INT64_MAX) return false;
candidate.expected = (int64_t)get(wire + 1, 8);
if (candidate.operation != PURE_PEER_ACTION) {
if (length != 9) return false;
} else {
if (length != 12 && length != 20 && length != 28) return false;
candidate.action.input = wire[9] == 255 ? UINT32_MAX : wire[9];
candidate.action.phase = wire[10];
candidate.action.target = wire[11] == 255 ? UINT32_MAX : wire[11];
if (length >= 20) candidate.action.x = get_double(wire + 12);
if (length == 28) candidate.action.y = get_double(wire + 20);
if (!valid_action(plan, &candidate.action, length)) return false;
}
}
*out = candidate;
return true;
}
/* These numbers belong to the wire version; do not serialize the C enum. */
static unsigned char wire_status(PureResult code) {
switch (code) {
case PURE_OK: return 0; case PURE_NOT_FOUND: return 1; case PURE_CONFLICT: return 2;
case PURE_BUSY: return 3; case PURE_BUDGET: return 4; case PURE_LIMIT: return 5;
case PURE_STORE_ERROR: return 6; case PURE_UNCERTAIN: return 7; case PURE_INVALID: return 8;
case PURE_DENIED: return 9; case PURE_CLOSED: return 10; case PURE_CANCELLED: return 11;
case PURE_ALLOCATION: return 12;
}
return 8;
}
size_t pure_peer_encode_reply(const PureNativeApp *app, PureResult status,
unsigned char out[PURE_PEER_REPLY_MAX]) {
if (!app || !out || app->plan->state_count > 32 || app->revision < 0 || app->stored_revision < 0) return 0;
out[0] = wire_status(status);
put(out + 1, (uint64_t)app->revision, 8);
put(out + 9, (uint64_t)app->stored_revision, 8);
put(out + 17, app->last_scalar_error, 4);
put(out + 21, app->held, 4);
out[25] = (unsigned char)app->plan->state_count;
for (uint32_t i = 0; i < app->plan->state_count; ++i)
put_double(out + 26 + i * 8, pure_scalar_get_state(app->scalar, i));
return 26 + 8u * app->plan->state_count;
}
bool pure_peer_decode_reply(const unsigned char *wire, size_t length, const PureScalarPlan *plan,
PurePeerReply *out) {
if (!wire || !plan || !out || length < 26 || plan->state_count > 32 || wire[0] > 12 ||
wire[25] != plan->state_count || length != 26 + 8u * plan->state_count) return false;
uint64_t revision = get(wire + 1, 8), stored = get(wire + 9, 8);
if (revision > INT64_MAX || stored > INT64_MAX) return false;
PurePeerReply candidate = {.status = wire[0], .revision = (int64_t)revision,
.stored_revision = (int64_t)stored, .scalar_error = (uint32_t)get(wire + 17, 4),
.held = (uint32_t)get(wire + 21, 4), .state_count = plan->state_count};
for (uint32_t i = 0; i < plan->state_count; ++i) {
candidate.states[i] = get_double(wire + 26 + i * 8);
if (!isfinite(candidate.states[i])) return false;
}
*out = candidate;
return true;
}
|