internal static string ToStringHelper(CBORObject obj, int depth) { StringBuilder sb = null; string simvalue = null; CBORType type = obj.Type; CBORObject curobject; if (obj.IsTagged) { if (sb == null) { if (type == CBORType.TextString) { // The default capacity of StringBuilder may be too small // for many strings, so set a suggested capacity // explicitly string str = obj.AsString(); sb = new StringBuilder(Math.Min(str.Length, 4096) + 16); } else { sb = new StringBuilder(); } } // Append opening tags if needed curobject = obj; while (curobject.IsTagged) { EInteger ei = curobject.MostOuterTag; sb.Append(ei.ToString()); sb.Append('('); curobject = curobject.UntagOne(); } } switch (type) { case CBORType.SimpleValue: sb = sb ?? new StringBuilder(); if (obj.IsUndefined) { sb.Append("undefined"); } else if (obj.IsNull) { sb.Append("null"); } else { sb.Append("simple("); int thisItemInt = obj.SimpleValue; char c; if (thisItemInt >= 100) { // NOTE: '0'-'9' have ASCII code 0x30-0x39 c = (char)(0x30 + ((thisItemInt / 100) % 10)); sb.Append(c); } if (thisItemInt >= 10) { c = (char)(0x30 + ((thisItemInt / 10) % 10)); sb.Append(c); c = (char)(0x30 + (thisItemInt % 10)); } else { c = (char)(0x30 + thisItemInt); } sb.Append(c); sb.Append(')'); } break; case CBORType.Boolean: case CBORType.Integer: simvalue = obj.Untag().ToJSONString(); if (sb == null) { return(simvalue); } sb.Append(simvalue); break; case CBORType.FloatingPoint: { long bits = obj.AsDoubleBits(); simvalue = bits == DoubleNegInfinity ? "-Infinity" : ( bits == DoublePosInfinity ? "Infinity" : ( CBORUtilities.DoubleBitsNaN(bits) ? "NaN" : obj.Untag().ToJSONString())); if (sb == null) { return(simvalue); } sb.Append(simvalue); break; } case CBORType.ByteString: { sb = sb ?? new StringBuilder(); sb.Append("h'"); byte[] data = obj.GetByteString(); int length = data.Length; for (var i = 0; i < length; ++i) { sb.Append(HexAlphabet[(data[i] >> 4) & 15]); sb.Append(HexAlphabet[data[i] & 15]); } sb.Append((char)0x27); break; } case CBORType.TextString: { sb = sb == null ? new StringBuilder() : sb; sb.Append('\"'); string ostring = obj.AsString(); sb.Append(ostring); sb.Append('\"'); break; } case CBORType.Array: { sb = sb ?? new StringBuilder(); var first = true; sb.Append('['); if (depth >= 50) { sb.Append("..."); } else { for (var i = 0; i < obj.Count; ++i) { if (!first) { sb.Append(", "); } sb.Append(ToStringHelper(obj[i], depth + 1)); first = false; } } sb.Append(']'); break; } case CBORType.Map: { sb = sb ?? new StringBuilder(); var first = true; sb.Append('{'); if (depth >= 50) { sb.Append("..."); } else { ICollection <KeyValuePair <CBORObject, CBORObject> > entries = obj.Entries; foreach (KeyValuePair <CBORObject, CBORObject> entry in entries) { CBORObject key = entry.Key; CBORObject value = entry.Value; if (!first) { sb.Append(", "); } sb.Append(ToStringHelper(key, depth + 1)); sb.Append(": "); sb.Append(ToStringHelper(value, depth + 1)); first = false; } } sb.Append('}'); break; } default: { sb = sb ?? new StringBuilder(); sb.Append("???"); break; } } // Append closing tags if needed curobject = obj; while (curobject.IsTagged) { sb.Append(')'); curobject = curobject.UntagOne(); } return(sb.ToString()); }
private static byte[] CtapCanonicalEncode(CBORObject a, int depth) { CBORObject cbor = a.Untag(); CBORType valueAType = cbor.Type; try { if (valueAType == CBORType.Array) { using (var ms = new MemoryStream()) { CBORObject.WriteValue(ms, 4, cbor.Count); for (var i = 0; i < cbor.Count; ++i) { if (depth >= 3 && IsArrayOrMap(cbor[i])) { throw new CBORException("Nesting level too deep"); } byte[] bytes = CtapCanonicalEncode(cbor[i], depth + 1); ms.Write(bytes, 0, bytes.Length); } return(ms.ToArray()); } } else if (valueAType == CBORType.Map) { KeyValuePair <byte[], byte[]> kv1; List <KeyValuePair <byte[], byte[]> > sortedKeys; sortedKeys = new List <KeyValuePair <byte[], byte[]> >(); foreach (CBORObject key in cbor.Keys) { if (depth >= 3 && (IsArrayOrMap(key) || IsArrayOrMap(cbor[key]))) { throw new CBORException("Nesting level too deep"); } CheckDepth(key, depth + 1); CheckDepth(cbor[key], depth + 1); // Check if key and value can be canonically encoded // (will throw an exception if they cannot) kv1 = new KeyValuePair <byte[], byte[]>( CtapCanonicalEncode(key, depth + 1), CtapCanonicalEncode(cbor[key], depth + 1)); sortedKeys.Add(kv1); } sortedKeys.Sort(ByteComparer); using (var ms = new MemoryStream()) { CBORObject.WriteValue(ms, 5, cbor.Count); byte[] lastKey = null; for (var i = 0; i < sortedKeys.Count; ++i) { kv1 = sortedKeys[i]; byte[] bytes = kv1.Key; if (lastKey != null && ByteArraysEqual(bytes, lastKey)) { throw new CBORException("duplicate canonical CBOR key"); } lastKey = bytes; ms.Write(bytes, 0, bytes.Length); bytes = kv1.Value; ms.Write(bytes, 0, bytes.Length); } return(ms.ToArray()); } } } catch (IOException ex) { throw new InvalidOperationException(ex.ToString(), ex); } if (valueAType == CBORType.SimpleValue || valueAType == CBORType.Boolean || valueAType == CBORType.ByteString || valueAType == CBORType.TextString) { return(cbor.EncodeToBytes(CBOREncodeOptions.Default)); } else if (valueAType == CBORType.FloatingPoint) { long bits = cbor.AsDoubleBits(); return(new byte[] { (byte)0xfb, (byte)((bits >> 56) & 0xffL), (byte)((bits >> 48) & 0xffL), (byte)((bits >> 40) & 0xffL), (byte)((bits >> 32) & 0xffL), (byte)((bits >> 24) & 0xffL), (byte)((bits >> 16) & 0xffL), (byte)((bits >> 8) & 0xffL), (byte)(bits & 0xffL), }); } else if (valueAType == CBORType.Integer) { return(cbor.EncodeToBytes(CBOREncodeOptions.Default)); } else { throw new ArgumentException("Invalid CBOR type."); } }