Encoder requires a codingPath property which returns an array of CodingKey values indicating the current path into the encoder. Since this encoder doesn't really support keys in the first place, we always return an empty array:.
Code which uses this class will have to be implemented not to require this value to make any sense. We'll have to implement all of those one by one. Let's start with the last one, which handles generic Encodable values. It just needs to call through to BinaryEncoder 's encode method:. We can use a similar technique to implement the other methods, and All of the compiler errors about protocol conformance have gone away?
It turns out that this one implementation of encode satisfies all of the encode methods in the protocol, because all of the other types are Encodable. A suitable generic method will fulfill any matching protocol requirements. It's obvious in retrospect, but I didn't realize it until I was halfway done with this code and saw that errors didn't appear when I deleted type-specific methods. Now we can see why I implemented BinaryEncoder 's encode method with a big switch statement instead of using separate implementations for all of the various supported types.
Overloaded methods are resolved at compile time based on the static type that's available at the call site. The above call to encoder. Encodable even if the actual value passed in is, say, a Double or a Bool.
In order to allow for this simple wrapper, the implementation in BinaryEncoder has to work with a single entry point, which means it needs to be a big switch statement. KeyedEncodingContainerProtocol requires a few other methods.
There's one for encoding nil, which we implement to do nothing:. Then there are four methods for returning nested containers or superclass encoders. We don't do anything clever here, so this just delegates back to the encoder:. It turns out that those protocols are similar enough that we can use a single implementation for both. The actual implementation is almost the same as it was for KeyedEncodingContainerProtocol , with the addition of a dummy count property:.
It also requires a userInfo property. We don't support that either, so it returns an empty dictionary:. Unlike the encoder, the decoder's data is loaded into the object when it's created.
The caller provides the data that the decoder will decode from:. The decoder also needs to keep track of where it is inside the data it's decoding. It does that with a cursor property, which starts out at the beginning of the data:. The decoder has its own errors it can throw during the decoding process. Decoding can fail in many more ways than encoding, so BinaryDecoder 's Error type has a lot more cases:.
Now we can get on to actual decoding. The lowest level method reads a certain number of bytes out of data into a pointer, advancing cursor , or throwing prematureEndOfData if data doesn't have enough bytes in it:. There's also a small generic wrapper which takes an inout T and reads into that value, using MemoryLayout to figure out how many bytes to read. Like BinaryEncoder , BinaryDecoder has methods for decoding floating-point types. For these, it creates an empty CFSwappedFloat value, reads into it, and then calls the appropriate CF function to convert it to the floating-point type in question:.
The method for decoding Bool decodes a UInt8 and then returns false if it's 0 , true if it's 1 , and otherwise throws an error:. The general decode method for Decodable uses a big switch statement to decode various specific types:. The compiler doesn't realize that T 's type must match the values being produced, so the as!
T convinces it to compile this code. Float , Double , and Bool all call their type-specific decoding methods:. BinaryDecodable types use the initializer defined in that protocol, passing self:. The FixedWidthInteger method uses Self. BinaryDecoder Decoder Implementation As before, we implement the three container protocols.
We'll start with the keyed container:. It also requires allKeys , which returns all keys that the container knows about.
Since we don't really support keys in the first place, this returns an empty array:. There's also a method to see if the container contains a given key. We'll just blindly say "yes" to all such questions:. As before, KeyedDecodingContainerProtocol has a ton of different decode methods which can all be satisfied with a single generic method for Decodable:.
Now BinaryDecoder itself can provide dummy implementations of the properties required by Decoder and implement methods to return instances of the containers:.
In theory I could call through to their Codable implementation, but I can't count on that implementation to work with the limitations of the binary coders, and I wouldn't have control over the serialized representation. Instead, I manually implemented it. The plan is to have Array encode its count, and then encode its elements. To decode, it can decode the count, then decode that many elements.
String will convert itself to UTF-8 in the form of Array and then use Array 's implementation to do the real work. Someday, when Swift gets conditional conformances , we'll be able to write extension Array: BinaryCodable to indicate that Array is is only codable when its contents are.
For now, Swift can't express that notion. Instead, we have to say that Array is always BinaryCodable , and then do runtime type checks to ensure the content is suitable. Encoding is a matter of checking the type of Element , encoding self. String can then encode itself by creating an Array from its utf8 property and encoding that:.
This will fail if the decoded Array isn't valid UTF-8, so there's a little extra code here to check for that and throw an error:. Example Use That takes care of binary encoding and decoding.
Conclusion Swift's new Codable protocols are a welcome addition to the language to eliminate a lot of boilerplate code. Unsophisticated binary formats such as this are not often called for, but they have their uses, and it's interesting to see how Codable can be used for something so different from the built-in facilities.
The Encoder and Decoder protocols are large, but judicious use of generics can cut down a lot of the repetitive code, and implementation is relatively simple in the end.
BinaryCoder was written for exploratory and educational purposes, and it's probably not what you want to use in your own programs. However, there are cases where it could be suitable, as long as you understand the tradeoffs involved. That's it for today! Come back again for more exciting byte-related adventures. Getting Answers Ten simple points to follow to get good answers on IRC, mailing lists, and other places.
Miscellaneous Pages Miscellaneous old, rarely-updated content. Posted at This article is also available in Hungarian translation by Zsolt Boros. Bool , forKey key: Int , forKey key: Int8 , forKey key: Int16 , forKey key: Int32 , forKey key: Int64 , forKey key: UInt , forKey key: UInt8 , forKey key: UInt16 , forKey key: UInt32 , forKey key: It displays the encoded string and the image in the output file. Description Converts binary data to a string.
Returns An encoded string representing the binary data. Category Conversion functions , String functions. Function syntax BinaryEncode binarydata , encoding. History ColdFusion MX 7: Parameters Parameter Description binarydata A variable containing the binary data to encode. Usage Binary objects and, in some cases, 8-bit characters, cannot be transported over many Internet protocols, such as HTTP and SMTP, and might not be supported by some database systems.
See the following pages for additional information on handling binary data: Example The following example reads a GIF file as binary data, converts it to a binary-encoded string, converts the binary-encoded data back to binary data, and writes the result to a file. Parameters Parameter Description binarydata.