implement sorting on map keys v1 SUPERSEDED

Emily Martins: 1
 implement sorting on map keys

 6 files changed, 70 insertions(+), 8 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~williewillus/racket-libraries/patches/40187/mbox | git am -3
Learn more about email & git

[PATCH] implement sorting on map keys Export this patch

Hi Vincent,

Here's a patch for allowing the sorting of map keys when encoding cbor  
values. I thought about extending the functionality to allow arbitrary
sorting. But for now, this works quite well for such a niche use-case.

I'd be willing to help maintain this package as it's something I have to
use quite frequently in various of the projects I'm involved with. If   
you'd prefer, I'm available to talk on Discord too.


 common.rkt               | 12 ++++++++++--
 encode.rkt               | 12 +++++++++---
 flake.lock               | 25 +++++++++++++++++++++++++
 flake.nix                | 15 +++++++++++++++
 info.rkt                 |  2 +-
 scribblings/manual.scrbl | 12 ++++++++++--
 6 files changed, 70 insertions(+), 8 deletions(-)
 create mode 100644 flake.lock
 create mode 100644 flake.nix

diff --git a/common.rkt b/common.rkt
index 979fb0a..79f9370 100644
--- a/common.rkt
+++ b/common.rkt
@@ -31,9 +31,11 @@
(provide cbor-config?
          [cbor-empty-config cbor-config?]
          [with-cbor-null (-> cbor-config? any/c cbor-config?)]
          [with-sorted-map-keys (-> cbor-config? boolean? cbor-config?)]
          [with-cbor-tag-deserializer (-> cbor-config?
                                          (-> cbor-valid-tag-number? any/c any/c)
@@ -41,9 +43,10 @@

(struct cbor-config

(define cbor-empty-config (cbor-config #hasheqv() 'null))
(define cbor-empty-config (cbor-config #hasheqv() 'null #f))

(define (with-cbor-tag-deserializer config id deser)
  (define old-handlers (cbor-config-tag-deserializers config))
@@ -55,3 +58,8 @@
   cbor-config config
   [null-value v]))

(define (with-sorted-map-keys config sorted)
   cbor-config config
   [sorted-map-keys sorted]))
diff --git a/encode.rkt b/encode.rkt
index 4439a73..b9a09a6 100644
--- a/encode.rkt
+++ b/encode.rkt
@@ -3,6 +3,7 @@
(require racket/generic
(require racket/match)
(provide cbor-write
@@ -93,9 +94,14 @@
(define (cbor-write-map config m out)
  (define len (hash-count m))
  (write-argument out 5 len)
  (hash-for-each m (lambda (k v)
                     (cbor-write config k out)
                     (cbor-write config v out)))
  (if (cbor-config-sorted-map-keys config)
      (for-each (match-lambda
                  [(cons k v)
                   (cbor-write config k out)
                   (cbor-write config v out)]) (sort (hash->list m) < #:key car))
      (hash-for-each m (lambda (k v)
                         (cbor-write config k out)
                         (cbor-write config v out))))
  (when (> len u64-max)
    (write-break out)))

diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..41767a0
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,25 @@
  "nodes": {
    "nixpkgs": {
      "locked": {
        "lastModified": 1680668850,
        "narHash": "sha256-mQMg13yRsS0LXVzaeoSPwqgPO6yhkGzGewPgMSqXSv8=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "4a65e9f64e53fdca6eed31adba836717a11247d2",
        "type": "github"
      "original": {
        "id": "nixpkgs",
        "type": "indirect"
    "root": {
      "inputs": {
        "nixpkgs": "nixpkgs"
  "root": "root",
  "version": 7
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..0985146
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,15 @@
  description = "Racket exploration env";

  outputs = { self, nixpkgs }:
      pkgs = import nixpkgs { system = "x86_64-linux"; };
      devShell.x86_64-linux = pkgs.mkShell {
        buildInputs = [
diff --git a/info.rkt b/info.rkt
index 3b94a5c..21104fa 100644
--- a/info.rkt
+++ b/info.rkt
@@ -1,6 +1,6 @@
#lang info
(define collection "cbor")
(define version "0.9")
(define version "0.10")
(define deps '("base"))
(define build-deps '("racket-doc"
diff --git a/scribblings/manual.scrbl b/scribblings/manual.scrbl
index 21e0047..0009606 100644
--- a/scribblings/manual.scrbl
+++ b/scribblings/manual.scrbl
@@ -55,12 +55,14 @@ Major types are decoded as follows:

@section{Configuration Options}
@defstruct[cbor-config ((tag-deserializers (hash/c cbor-valid-tag-number? (-> cbor-valid-tag-number? any/c any/c))) (null-value any/c))]{
@defstruct[cbor-config ((tag-deserializers (hash/c cbor-valid-tag-number? (-> cbor-valid-tag-number? any/c any/c))) (null-value any/c) (sorted-map-keys boolean?))]{
Configuration object for CBOR encoding and decoding. Note that this struct is only exposed opaquely, and must be manipulated with the provided functions and values.

@racket[tag-deserializers] is a hash from CBOR tag numbers to a procedure that takes the CBOR tag number and a raw data value and produces the meaningful interpretation of that value.

@racket[null-value] is the value that CBOR @tt{null} will be encoded and decoded as.

@racket[sorted-map-keys] determines whether or not map keys should appear in sorted order. This only affects encoding; unsorted map keys will still be parsed identically. Keep in mind, this functionality should not be depended on by any decoders, but for legacy purposes some decoders don't parse properly otherwise.

@defthing[cbor-empty-config cbor-config?]{
@@ -80,6 +82,12 @@ Registers a tag deserializer to the given config, returning a new config.
Registers a null value to the given config, returning a new config.

@defproc[(with-sorted-map-keys [config cbor-config?]
                               [v boolean?])
Set whether map keys should be sorted.

@defthing[gen:cbor-custom-write any/c]{
A @tech{generic interface} that supplies a method, @racket[cbor-write-proc] that can serialize arbitrary values to CBOR. Implementations of the method should accept a value to be serialized and return it in serialized form as a @racket[cbor-tag?].
@@ -113,4 +121,4 @@ Patches to improve these issues are welcome! Email patches to @tt{~williewillus/
@item{There should be a config or parameter to specify maximum lengths for indefinite-length objects}
@item{More correctness-testing is needed.}
@item{Some recommended tag and simple values from the RFC are not supported yet.}
\ No newline at end of file