 public extension OrderedCodingKey where Self : CaseIterable & Equatable {
     /// The default key ordering for an `OrderedCodingKey` is the position of this key in its `allCases` list.
     /// - See Also: `Encodable.encodedStringOrdered`
+    /// - Complexity: O(N) on the number of keys
     var keyOrder: Self.AllCases.Index? {
+        // this could be optimized by caching a [Self: Index] in the type, but we'd need to manually cache it separately in each type since protocol extension cannot have stored static properties
         Self.allCases.firstIndex(of: self)
     }
 }

    ///
    /// - See Also: `OrderedCodingKey`
    /// - Note: Unlike `JSONEncoder.encode`, this function is not thread-safe due to needing to modify internal properties of the `JSONEncoder`.
    func encodeOrdered<T: Encodable>(_ value: T) throws -> Data {
        // we need to sort the keys in order for our replacement scheme to work
        let hadSorted = self.outputFormatting.contains(.sortedKeys)

        if let orderPrefixData = orderPrefix.data(using: .utf8) { // JSON encoding is always UTF8
            for i in data.indices.reversed() { // go from back to front so we can remove data chunks with impunity
                if data[i...].starts(with: orderPrefixData) { // Data slice subscript is complexity: O(1)
                    data.removeSubrange(i..<i+keylen) // remove the prefix, along with the key number; the prefix is a fixed length, so we can simply cut out the raw bytes without needing to look at the contents
                }
            }
        }
