Coverage Report

Created: 2023-11-19 06:57

/src/jq/src/jv_aux.c
Line
Count
Source (jump to first uncovered line)
1
#include <assert.h>
2
#include <limits.h>
3
#include <math.h>
4
#include <string.h>
5
#include <stdlib.h>
6
#include "jv_alloc.h"
7
#include "jv_private.h"
8
9
// making this static verbose function here
10
// until we introduce a less confusing naming scheme
11
// of jv_* API with regards to the memory management
12
0
static double jv_number_get_value_and_consume(jv number) {
13
0
  double value = jv_number_value(number);
14
0
  jv_free(number);
15
0
  return value;
16
0
}
17
18
0
static jv parse_slice(jv j, jv slice, int* pstart, int* pend) {
19
  // Array slices
20
0
  jv start_jv = jv_object_get(jv_copy(slice), jv_string("start"));
21
0
  jv end_jv = jv_object_get(slice, jv_string("end"));
22
0
  if (jv_get_kind(start_jv) == JV_KIND_NULL) {
23
0
    jv_free(start_jv);
24
0
    start_jv = jv_number(0);
25
0
  }
26
0
  int len;
27
0
  if (jv_get_kind(j) == JV_KIND_ARRAY) {
28
0
    len = jv_array_length(j);
29
0
  } else if (jv_get_kind(j) == JV_KIND_STRING) {
30
0
    len = jv_string_length_codepoints(j);
31
0
  } else {
32
    /*
33
     * XXX This should be dead code because callers shouldn't call this
34
     * function if `j' is neither an array nor a string.
35
     */
36
0
    jv_free(j);
37
0
    jv_free(start_jv);
38
0
    jv_free(end_jv);
39
0
    return jv_invalid_with_msg(jv_string("Only arrays and strings can be sliced"));
40
0
  }
41
0
  if (jv_get_kind(end_jv) == JV_KIND_NULL) {
42
0
    jv_free(end_jv);
43
0
    end_jv = jv_number(len);
44
0
  }
45
0
  if (jv_get_kind(start_jv) != JV_KIND_NUMBER ||
46
0
      jv_get_kind(end_jv) != JV_KIND_NUMBER) {
47
0
    jv_free(start_jv);
48
0
    jv_free(end_jv);
49
0
    return jv_invalid_with_msg(jv_string("Array/string slice indices must be integers"));
50
0
  }
51
52
0
  double dstart = jv_number_value(start_jv);
53
0
  double dend = jv_number_value(end_jv);
54
0
  int start, end;
55
56
0
  jv_free(start_jv);
57
0
  jv_free(end_jv);
58
0
  if (isnan(dstart)) dstart = 0;
59
0
  if (dstart < 0)    dstart += len;
60
0
  if (dstart < 0)    dstart = 0;
61
0
  if (dstart > len)  dstart = len;
62
0
  start = dstart > INT_MAX ? INT_MAX : (int)dstart; // Rounds down
63
64
0
  if (isnan(dend))   dend = len;
65
0
  if (dend < 0)      dend += len;
66
0
  if (dend < 0)      dend  = start;
67
0
  end = dend > INT_MAX ? INT_MAX : (int)dend;
68
0
  if (end > len)     end = len;
69
0
  if (end < len)     end += end < dend ? 1 : 0; // We round start down
70
                                                // but round end up
71
72
0
  if (end < start) end = start;
73
0
  assert(0 <= start && start <= end && end <= len);
74
0
  *pstart = start;
75
0
  *pend = end;
76
0
  return jv_true();
77
0
}
78
79
0
jv jv_get(jv t, jv k) {
80
0
  jv v;
81
0
  if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) {
82
0
    v = jv_object_get(t, k);
83
0
    if (!jv_is_valid(v)) {
84
0
      jv_free(v);
85
0
      v = jv_null();
86
0
    }
87
0
  } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
88
0
    if (jvp_number_is_nan(k)) {
89
0
      jv_free(t);
90
0
      v = jv_null();
91
0
    } else {
92
0
      double didx = jv_number_value(k);
93
0
      if (jvp_number_is_nan(k)) {
94
0
        v = jv_null();
95
0
      } else {
96
0
        if (didx < INT_MIN) didx = INT_MIN;
97
0
        if (didx > INT_MAX) didx = INT_MAX;
98
0
        int idx = (int)didx;
99
0
        if (idx < 0)
100
0
          idx += jv_array_length(jv_copy(t));
101
0
        v = jv_array_get(t, idx);
102
0
        if (!jv_is_valid(v)) {
103
0
          jv_free(v);
104
0
          v = jv_null();
105
0
        }
106
0
      }
107
0
    }
108
0
    jv_free(k);
109
0
  } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) {
110
0
    int start, end;
111
0
    jv e = parse_slice(jv_copy(t), k, &start, &end);
112
0
    if (jv_get_kind(e) == JV_KIND_TRUE) {
113
0
      v = jv_array_slice(t, start, end);
114
0
    } else {
115
0
      jv_free(t);
116
0
      v = e;
117
0
    }
118
0
  } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) {
119
0
    int start, end;
120
0
    jv e = parse_slice(jv_copy(t), k, &start, &end);
121
0
    if (jv_get_kind(e) == JV_KIND_TRUE) {
122
0
      v = jv_string_slice(t, start, end);
123
0
    } else {
124
0
      jv_free(t);
125
0
      v = e;
126
0
    }
127
0
  } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) {
128
0
    v = jv_array_indexes(t, k);
129
0
  } else if (jv_get_kind(t) == JV_KIND_NULL &&
130
0
             (jv_get_kind(k) == JV_KIND_STRING ||
131
0
              jv_get_kind(k) == JV_KIND_NUMBER ||
132
0
              jv_get_kind(k) == JV_KIND_OBJECT)) {
133
0
    jv_free(t);
134
0
    jv_free(k);
135
0
    v = jv_null();
136
0
  } else {
137
    /*
138
     * If k is a short string it's probably from a jq .foo expression or
139
     * similar, in which case putting it in the invalid msg may help the
140
     * user.  The length 30 is arbitrary.
141
     */
142
0
    if (jv_get_kind(k) == JV_KIND_STRING && jv_string_length_bytes(jv_copy(k)) < 30) {
143
0
      v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with string \"%s\"",
144
0
                                            jv_kind_name(jv_get_kind(t)),
145
0
                                            jv_string_value(k)));
146
0
    } else {
147
0
      v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
148
0
                                            jv_kind_name(jv_get_kind(t)),
149
0
                                            jv_kind_name(jv_get_kind(k))));
150
0
    }
151
0
    jv_free(t);
152
0
    jv_free(k);
153
0
  }
154
0
  return v;
155
0
}
156
157
0
jv jv_set(jv t, jv k, jv v) {
158
0
  if (!jv_is_valid(v)) {
159
0
    jv_free(t);
160
0
    jv_free(k);
161
0
    return v;
162
0
  }
163
0
  int isnull = jv_get_kind(t) == JV_KIND_NULL;
164
0
  if (jv_get_kind(k) == JV_KIND_STRING &&
165
0
      (jv_get_kind(t) == JV_KIND_OBJECT || isnull)) {
166
0
    if (isnull) t = jv_object();
167
0
    t = jv_object_set(t, k, v);
168
0
  } else if (jv_get_kind(k) == JV_KIND_NUMBER &&
169
0
             (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) {
170
0
    if (jvp_number_is_nan(k)) {
171
0
      jv_free(t);
172
0
      jv_free(k);
173
0
      t = jv_invalid_with_msg(jv_string("Cannot set array element at NaN index"));
174
0
    } else {
175
0
      double didx = jv_number_value(k);
176
0
      if (didx < INT_MIN) didx = INT_MIN;
177
0
      if (didx > INT_MAX) didx = INT_MAX;
178
0
      if (isnull) t = jv_array();
179
0
      t = jv_array_set(t, (int)didx, v);
180
0
      jv_free(k);
181
0
    }
182
0
  } else if (jv_get_kind(k) == JV_KIND_OBJECT &&
183
0
             (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) {
184
0
    if (isnull) t = jv_array();
185
0
    int start, end;
186
0
    jv e = parse_slice(jv_copy(t), k, &start, &end);
187
0
    if (jv_get_kind(e) == JV_KIND_TRUE) {
188
0
      if (jv_get_kind(v) == JV_KIND_ARRAY) {
189
0
        int array_len = jv_array_length(jv_copy(t));
190
0
        assert(0 <= start && start <= end && end <= array_len);
191
0
        int slice_len = end - start;
192
0
        int insert_len = jv_array_length(jv_copy(v));
193
0
        if (slice_len < insert_len) {
194
          // array is growing
195
0
          int shift = insert_len - slice_len;
196
0
          for (int i = array_len - 1; i >= end; i--) {
197
0
            t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i));
198
0
          }
199
0
        } else if (slice_len > insert_len) {
200
          // array is shrinking
201
0
          int shift = slice_len - insert_len;
202
0
          for (int i = end; i < array_len; i++) {
203
0
            t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i));
204
0
          }
205
0
          t = jv_array_slice(t, 0, array_len - shift);
206
0
        }
207
0
        for (int i=0; i < insert_len; i++) {
208
0
          t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i));
209
0
        }
210
0
        jv_free(v);
211
0
      } else {
212
0
        jv_free(t);
213
0
        jv_free(v);
214
0
        t = jv_invalid_with_msg(jv_string_fmt("A slice of an array can only be assigned another array"));
215
0
      }
216
0
    } else {
217
0
      jv_free(t);
218
0
      jv_free(v);
219
0
      t = e;
220
0
    }
221
0
  } else if (jv_get_kind(k) == JV_KIND_OBJECT && jv_get_kind(t) == JV_KIND_STRING) {
222
0
    jv_free(t);
223
0
    jv_free(k);
224
0
    jv_free(v);
225
    /* Well, why not?  We should implement this... */
226
0
    t = jv_invalid_with_msg(jv_string_fmt("Cannot update string slices"));
227
0
  } else {
228
0
    jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s",
229
0
                                               jv_kind_name(jv_get_kind(k)),
230
0
                                               jv_kind_name(jv_get_kind(t))));
231
0
    jv_free(t);
232
0
    jv_free(k);
233
0
    jv_free(v);
234
0
    t = err;
235
0
  }
236
0
  return t;
237
0
}
238
239
0
jv jv_has(jv t, jv k) {
240
0
  assert(jv_is_valid(t));
241
0
  assert(jv_is_valid(k));
242
0
  jv ret;
243
0
  if (jv_get_kind(t) == JV_KIND_NULL) {
244
0
    jv_free(t);
245
0
    jv_free(k);
246
0
    ret = jv_false();
247
0
  } else if (jv_get_kind(t) == JV_KIND_OBJECT &&
248
0
             jv_get_kind(k) == JV_KIND_STRING) {
249
0
    jv elem = jv_object_get(t, k);
250
0
    ret = jv_bool(jv_is_valid(elem));
251
0
    jv_free(elem);
252
0
  } else if (jv_get_kind(t) == JV_KIND_ARRAY &&
253
0
             jv_get_kind(k) == JV_KIND_NUMBER) {
254
0
    if (jvp_number_is_nan(k)) {
255
0
      jv_free(t);
256
0
      ret = jv_false();
257
0
    } else {
258
0
      jv elem = jv_array_get(t, (int)jv_number_value(k));
259
0
      ret = jv_bool(jv_is_valid(elem));
260
0
      jv_free(elem);
261
0
    }
262
0
    jv_free(k);
263
0
  } else {
264
0
    ret = jv_invalid_with_msg(jv_string_fmt("Cannot check whether %s has a %s key",
265
0
                                            jv_kind_name(jv_get_kind(t)),
266
0
                                            jv_kind_name(jv_get_kind(k))));
267
0
    jv_free(t);
268
0
    jv_free(k);
269
0
  }
270
0
  return ret;
271
0
}
272
273
// assumes keys is a sorted array
274
0
static jv jv_dels(jv t, jv keys) {
275
0
  assert(jv_get_kind(keys) == JV_KIND_ARRAY);
276
0
  assert(jv_is_valid(t));
277
278
0
  if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) {
279
    // no change
280
0
  } else if (jv_get_kind(t) == JV_KIND_ARRAY) {
281
    // extract slices, they must be handled differently
282
0
    jv neg_keys = jv_array();
283
0
    jv nonneg_keys = jv_array();
284
0
    jv new_array = jv_array();
285
0
    jv starts = jv_array(), ends = jv_array();
286
0
    jv_array_foreach(keys, i, key) {
287
0
      if (jv_get_kind(key) == JV_KIND_NUMBER) {
288
0
        if (jv_number_value(key) < 0) {
289
0
          neg_keys = jv_array_append(neg_keys, key);
290
0
        } else {
291
0
          nonneg_keys = jv_array_append(nonneg_keys, key);
292
0
        }
293
0
      } else if (jv_get_kind(key) == JV_KIND_OBJECT) {
294
0
        int start, end;
295
0
        jv e = parse_slice(jv_copy(t), key, &start, &end);
296
0
        if (jv_get_kind(e) == JV_KIND_TRUE) {
297
0
          starts = jv_array_append(starts, jv_number(start));
298
0
          ends = jv_array_append(ends, jv_number(end));
299
0
        } else {
300
0
          jv_free(new_array);
301
0
          jv_free(key);
302
0
          new_array = e;
303
0
          goto arr_out;
304
0
        }
305
0
      } else {
306
0
        jv_free(new_array);
307
0
        new_array = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array",
308
0
                                                      jv_kind_name(jv_get_kind(key))));
309
0
        jv_free(key);
310
0
        goto arr_out;
311
0
      }
312
0
    }
313
314
0
    int neg_idx = 0;
315
0
    int nonneg_idx = 0;
316
0
    int len = jv_array_length(jv_copy(t));
317
0
    jv_array_foreach(t, i, elem) {
318
0
      int del = 0;
319
0
      while (neg_idx < jv_array_length(jv_copy(neg_keys))) {
320
0
        int delidx = len + (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(neg_keys), neg_idx));
321
0
        if (i == delidx) {
322
0
          del = 1;
323
0
        }
324
0
        if (i < delidx) {
325
0
          break;
326
0
        }
327
0
        neg_idx++;
328
0
      }
329
0
      while (nonneg_idx < jv_array_length(jv_copy(nonneg_keys))) {
330
0
        int delidx = (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(nonneg_keys), nonneg_idx));
331
0
        if (i == delidx) {
332
0
          del = 1;
333
0
        }
334
0
        if (i < delidx) {
335
0
          break;
336
0
        }
337
0
        nonneg_idx++;
338
0
      }
339
0
      for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) {
340
0
        if ((int)jv_number_get_value_and_consume(jv_array_get(jv_copy(starts), sidx)) <= i &&
341
0
            i < (int)jv_number_get_value_and_consume(jv_array_get(jv_copy(ends), sidx))) {
342
0
          del = 1;
343
0
        }
344
0
      }
345
0
      if (!del)
346
0
        new_array = jv_array_append(new_array, elem);
347
0
      else
348
0
        jv_free(elem);
349
0
    }
350
0
  arr_out:
351
0
    jv_free(neg_keys);
352
0
    jv_free(nonneg_keys);
353
0
    jv_free(starts);
354
0
    jv_free(ends);
355
0
    jv_free(t);
356
0
    t = new_array;
357
0
  } else if (jv_get_kind(t) == JV_KIND_OBJECT) {
358
0
    jv_array_foreach(keys, i, k) {
359
0
      if (jv_get_kind(k) != JV_KIND_STRING) {
360
0
        jv_free(t);
361
0
        t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",
362
0
                                              jv_kind_name(jv_get_kind(k))));
363
0
        jv_free(k);
364
0
        break;
365
0
      }
366
0
      t = jv_object_delete(t, k);
367
0
    }
368
0
  } else {
369
0
    jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
370
0
                                               jv_kind_name(jv_get_kind(t))));
371
0
    jv_free(t);
372
0
    t = err;
373
0
  }
374
0
  jv_free(keys);
375
0
  return t;
376
0
}
377
378
0
jv jv_setpath(jv root, jv path, jv value) {
379
0
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
380
0
    jv_free(value);
381
0
    jv_free(root);
382
0
    jv_free(path);
383
0
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
384
0
  }
385
0
  if (!jv_is_valid(root)){
386
0
    jv_free(value);
387
0
    jv_free(path);
388
0
    return root;
389
0
  }
390
0
  if (jv_array_length(jv_copy(path)) == 0) {
391
0
    jv_free(path);
392
0
    jv_free(root);
393
0
    return value;
394
0
  }
395
0
  jv pathcurr = jv_array_get(jv_copy(path), 0);
396
0
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
397
398
  /*
399
   * We need to be careful not to make extra copies since that leads to
400
   * quadratic behavior (e.g., when growing large data structures in a
401
   * reduction with `setpath/2`, i.e., with `|=`.
402
   */
403
0
  if (jv_get_kind(pathcurr) == JV_KIND_OBJECT) {
404
    // Assignment to slice -- dunno yet how to avoid the extra copy
405
0
    return jv_set(root, pathcurr,
406
0
                  jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
407
0
  }
408
409
0
  jv subroot = jv_get(jv_copy(root), jv_copy(pathcurr));
410
0
  if (!jv_is_valid(subroot)) {
411
0
    jv_free(pathcurr);
412
0
    jv_free(pathrest);
413
0
    jv_free(value);
414
0
    return subroot;
415
0
  }
416
417
  // To avoid the extra copy we drop the reference from `root` by setting that
418
  // to null first.
419
0
  root = jv_set(root, jv_copy(pathcurr), jv_null());
420
0
  if (!jv_is_valid(root)) {
421
0
    jv_free(pathcurr);
422
0
    jv_free(pathrest);
423
0
    jv_free(value);
424
0
    return root;
425
0
  }
426
0
  return jv_set(root, pathcurr, jv_setpath(subroot, pathrest, value));
427
0
}
428
429
0
jv jv_getpath(jv root, jv path) {
430
0
  if (jv_get_kind(path) != JV_KIND_ARRAY) {
431
0
    jv_free(root);
432
0
    jv_free(path);
433
0
    return jv_invalid_with_msg(jv_string("Path must be specified as an array"));
434
0
  }
435
0
  if (!jv_is_valid(root)) {
436
0
    jv_free(path);
437
0
    return root;
438
0
  }
439
0
  if (jv_array_length(jv_copy(path)) == 0) {
440
0
    jv_free(path);
441
0
    return root;
442
0
  }
443
0
  jv pathcurr = jv_array_get(jv_copy(path), 0);
444
0
  jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
445
0
  return jv_getpath(jv_get(root, pathcurr), pathrest);
446
0
}
447
448
// assumes paths is a sorted array of arrays
449
0
static jv delpaths_sorted(jv object, jv paths, int start) {
450
0
  jv delkeys = jv_array();
451
0
  for (int i=0; i<jv_array_length(jv_copy(paths));) {
452
0
    int j = i;
453
0
    assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);
454
0
    int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;
455
0
    jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);
456
0
    while (j < jv_array_length(jv_copy(paths)) &&
457
0
           jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))
458
0
      j++;
459
    // if i <= entry < j, then entry starts with key
460
0
    if (delkey) {
461
      // deleting this entire key, we don't care about any more specific deletions
462
0
      delkeys = jv_array_append(delkeys, key);
463
0
    } else {
464
      // deleting certain sub-parts of this key
465
0
      jv subobject = jv_get(jv_copy(object), jv_copy(key));
466
0
      if (!jv_is_valid(subobject)) {
467
0
        jv_free(key);
468
0
        jv_free(object);
469
0
        object = subobject;
470
0
        break;
471
0
      } else if (jv_get_kind(subobject) == JV_KIND_NULL) {
472
0
        jv_free(key);
473
0
        jv_free(subobject);
474
0
      } else {
475
0
        jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);
476
0
        if (!jv_is_valid(newsubobject)) {
477
0
          jv_free(key);
478
0
          jv_free(object);
479
0
          object = newsubobject;
480
0
          break;
481
0
        }
482
0
        object = jv_set(object, key, newsubobject);
483
0
      }
484
0
      if (!jv_is_valid(object)) break;
485
0
    }
486
0
    i = j;
487
0
  }
488
0
  jv_free(paths);
489
0
  if (jv_is_valid(object))
490
0
    object = jv_dels(object, delkeys);
491
0
  else
492
0
    jv_free(delkeys);
493
0
  return object;
494
0
}
495
496
0
jv jv_delpaths(jv object, jv paths) {
497
0
  if (jv_get_kind(paths) != JV_KIND_ARRAY) {
498
0
    jv_free(object);
499
0
    jv_free(paths);
500
0
    return jv_invalid_with_msg(jv_string("Paths must be specified as an array"));
501
0
  }
502
0
  paths = jv_sort(paths, jv_copy(paths));
503
0
  jv_array_foreach(paths, i, elem) {
504
0
    if (jv_get_kind(elem) != JV_KIND_ARRAY) {
505
0
      jv_free(object);
506
0
      jv_free(paths);
507
0
      jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",
508
0
                                                 jv_kind_name(jv_get_kind(elem))));
509
0
      jv_free(elem);
510
0
      return err;
511
0
    }
512
0
    jv_free(elem);
513
0
  }
514
0
  if (jv_array_length(jv_copy(paths)) == 0) {
515
    // nothing is being deleted
516
0
    jv_free(paths);
517
0
    return object;
518
0
  }
519
0
  if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {
520
    // everything is being deleted
521
0
    jv_free(paths);
522
0
    jv_free(object);
523
0
    return jv_null();
524
0
  }
525
0
  return delpaths_sorted(object, paths, 0);
526
0
}
527
528
529
1.44k
static int string_cmp(const void* pa, const void* pb){
530
1.44k
  const jv* a = pa;
531
1.44k
  const jv* b = pb;
532
1.44k
  int lena = jv_string_length_bytes(jv_copy(*a));
533
1.44k
  int lenb = jv_string_length_bytes(jv_copy(*b));
534
1.44k
  int minlen = lena < lenb ? lena : lenb;
535
1.44k
  int r = memcmp(jv_string_value(*a), jv_string_value(*b), minlen);
536
1.44k
  if (r == 0) r = lena - lenb;
537
1.44k
  return r;
538
1.44k
}
539
540
153
jv jv_keys_unsorted(jv x) {
541
153
  if (jv_get_kind(x) != JV_KIND_OBJECT)
542
0
    return jv_keys(x);
543
153
  jv answer = jv_array_sized(jv_object_length(jv_copy(x)));
544
33.2k
  jv_object_foreach(x, key, value) {
545
33.2k
    answer = jv_array_append(answer, key);
546
33.2k
    jv_free(value);
547
33.2k
  }
548
153
  jv_free(x);
549
153
  return answer;
550
153
}
551
552
2.29k
jv jv_keys(jv x) {
553
2.29k
  if (jv_get_kind(x) == JV_KIND_OBJECT) {
554
2.29k
    int nkeys = jv_object_length(jv_copy(x));
555
2.29k
    jv* keys = jv_mem_calloc(sizeof(jv), nkeys);
556
2.29k
    int kidx = 0;
557
2.29k
    jv_object_foreach(x, key, value) {
558
1.68k
      keys[kidx++] = key;
559
1.68k
      jv_free(value);
560
1.68k
    }
561
2.29k
    qsort(keys, nkeys, sizeof(jv), string_cmp);
562
2.29k
    jv answer = jv_array_sized(nkeys);
563
3.98k
    for (int i = 0; i<nkeys; i++) {
564
1.68k
      answer = jv_array_append(answer, keys[i]);
565
1.68k
    }
566
2.29k
    jv_mem_free(keys);
567
2.29k
    jv_free(x);
568
2.29k
    return answer;
569
2.29k
  } else if (jv_get_kind(x) == JV_KIND_ARRAY) {
570
0
    int n = jv_array_length(x);
571
0
    jv answer = jv_array();
572
0
    for (int i=0; i<n; i++){
573
0
      answer = jv_array_set(answer, i, jv_number(i));
574
0
    }
575
0
    return answer;
576
0
  } else {
577
0
    assert(0 && "jv_keys passed something neither object nor array");
578
0
    return jv_invalid();
579
0
  }
580
2.29k
}
581
582
7.77k
int jv_cmp(jv a, jv b) {
583
7.77k
  if (jv_get_kind(a) != jv_get_kind(b)) {
584
755
    int r = (int)jv_get_kind(a) - (int)jv_get_kind(b);
585
755
    jv_free(a);
586
755
    jv_free(b);
587
755
    return r;
588
755
  }
589
7.02k
  int r = 0;
590
7.02k
  switch (jv_get_kind(a)) {
591
0
  default:
592
0
    assert(0 && "invalid kind passed to jv_cmp");
593
168
  case JV_KIND_NULL:
594
361
  case JV_KIND_FALSE:
595
511
  case JV_KIND_TRUE:
596
    // there's only one of each of these values
597
511
    r = 0;
598
511
    break;
599
600
2.74k
  case JV_KIND_NUMBER: {
601
2.74k
    if (jvp_number_is_nan(a)) {
602
160
      r = jv_cmp(jv_null(), jv_copy(b));
603
2.58k
    } else if (jvp_number_is_nan(b)) {
604
128
      r = jv_cmp(jv_copy(a), jv_null());
605
2.45k
    } else {
606
2.45k
      r = jvp_number_cmp(a, b);
607
2.45k
    }
608
2.74k
    break;
609
361
  }
610
611
506
  case JV_KIND_STRING: {
612
506
    r = string_cmp(&a, &b);
613
506
    break;
614
361
  }
615
616
2.11k
  case JV_KIND_ARRAY: {
617
    // Lexical ordering of arrays
618
2.11k
    int i = 0;
619
2.96k
    while (r == 0) {
620
2.93k
      int a_done = i >= jv_array_length(jv_copy(a));
621
2.93k
      int b_done = i >= jv_array_length(jv_copy(b));
622
2.93k
      if (a_done || b_done) {
623
2.08k
        r = b_done - a_done; //suddenly, logic
624
2.08k
        break;
625
2.08k
      }
626
848
      jv xa = jv_array_get(jv_copy(a), i);
627
848
      jv xb = jv_array_get(jv_copy(b), i);
628
848
      r = jv_cmp(xa, xb);
629
848
      i++;
630
848
    }
631
2.11k
    break;
632
361
  }
633
634
1.14k
  case JV_KIND_OBJECT: {
635
1.14k
    jv keys_a = jv_keys(jv_copy(a));
636
1.14k
    jv keys_b = jv_keys(jv_copy(b));
637
1.14k
    r = jv_cmp(jv_copy(keys_a), keys_b);
638
1.14k
    if (r == 0) {
639
770
      jv_array_foreach(keys_a, i, key) {
640
456
        jv xa = jv_object_get(jv_copy(a), jv_copy(key));
641
456
        jv xb = jv_object_get(jv_copy(b), key);
642
456
        r = jv_cmp(xa, xb);
643
456
        if (r) break;
644
456
      }
645
770
    }
646
1.14k
    jv_free(keys_a);
647
1.14k
    break;
648
361
  }
649
7.02k
  }
650
651
7.02k
  jv_free(a);
652
7.02k
  jv_free(b);
653
7.02k
  return r;
654
7.02k
}
655
656
657
struct sort_entry {
658
  jv object;
659
  jv key;
660
  int index;
661
};
662
663
0
static int sort_cmp(const void* pa, const void* pb) {
664
0
  const struct sort_entry* a = pa;
665
0
  const struct sort_entry* b = pb;
666
0
  int r = jv_cmp(jv_copy(a->key), jv_copy(b->key));
667
  // comparing by index if r == 0 makes the sort stable
668
0
  return r ? r : (a->index - b->index);
669
0
}
670
671
0
static struct sort_entry* sort_items(jv objects, jv keys) {
672
0
  assert(jv_get_kind(objects) == JV_KIND_ARRAY);
673
0
  assert(jv_get_kind(keys) == JV_KIND_ARRAY);
674
0
  assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
675
0
  int n = jv_array_length(jv_copy(objects));
676
0
  struct sort_entry* entries = jv_mem_calloc(sizeof(struct sort_entry), n);
677
0
  for (int i=0; i<n; i++) {
678
0
    entries[i].object = jv_array_get(jv_copy(objects), i);
679
0
    entries[i].key = jv_array_get(jv_copy(keys), i);
680
0
    entries[i].index = i;
681
0
  }
682
0
  jv_free(objects);
683
0
  jv_free(keys);
684
0
  qsort(entries, n, sizeof(struct sort_entry), sort_cmp);
685
0
  return entries;
686
0
}
687
688
0
jv jv_sort(jv objects, jv keys) {
689
0
  assert(jv_get_kind(objects) == JV_KIND_ARRAY);
690
0
  assert(jv_get_kind(keys) == JV_KIND_ARRAY);
691
0
  assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
692
0
  int n = jv_array_length(jv_copy(objects));
693
0
  struct sort_entry* entries = sort_items(objects, keys);
694
0
  jv ret = jv_array();
695
0
  for (int i=0; i<n; i++) {
696
0
    jv_free(entries[i].key);
697
0
    ret = jv_array_set(ret, i, entries[i].object);
698
0
  }
699
0
  jv_mem_free(entries);
700
0
  return ret;
701
0
}
702
703
0
jv jv_group(jv objects, jv keys) {
704
0
  assert(jv_get_kind(objects) == JV_KIND_ARRAY);
705
0
  assert(jv_get_kind(keys) == JV_KIND_ARRAY);
706
0
  assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
707
0
  int n = jv_array_length(jv_copy(objects));
708
0
  struct sort_entry* entries = sort_items(objects, keys);
709
0
  jv ret = jv_array();
710
0
  if (n > 0) {
711
0
    jv curr_key = entries[0].key;
712
0
    jv group = jv_array_append(jv_array(), entries[0].object);
713
0
    for (int i = 1; i < n; i++) {
714
0
      if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {
715
0
        jv_free(entries[i].key);
716
0
      } else {
717
0
        jv_free(curr_key);
718
0
        curr_key = entries[i].key;
719
0
        ret = jv_array_append(ret, group);
720
0
        group = jv_array();
721
0
      }
722
0
      group = jv_array_append(group, entries[i].object);
723
0
    }
724
0
    jv_free(curr_key);
725
0
    ret = jv_array_append(ret, group);
726
0
  }
727
0
  jv_mem_free(entries);
728
0
  return ret;
729
0
}