binary icon indicating copy to clipboard operation
binary copied to clipboard

Conditionals and complex bit handling

Open WhittlesJr opened this issue 6 years ago • 15 comments

Is there currently a way to use "conditional" fields? My use case is for the BACnet protocol, which is somewhat complex. Many fields are included in the spec that only show up if a previous field matches a certain value (or some other more complicated condition is met). Or sometimes, based on an earlier condition, the parsing rules for further segments will change...

I'm not sure how to do that with this library... maybe I'm missing something? I'm investigating header further to see if it can do everything I need, and if I find out I'll close this issue.

WhittlesJr avatar Aug 16 '18 19:08 WhittlesJr

Is it that you have to reify BinaryIO?

WhittlesJr avatar Aug 16 '18 19:08 WhittlesJr

Er, I think I can go off of select-codec from https://gist.github.com/stathissideris/8801295

... closing

WhittlesJr avatar Aug 16 '18 20:08 WhittlesJr

Er, then again, maybe not... that seems to be read-only.

I guess, could you give me an example that includes read and write, and has codec switching / selection logic?

WhittlesJr avatar Aug 16 '18 21:08 WhittlesJr

I'm also not sure how to cleanly handle a case where I have a byte that consists of 6 boolean bits and then 2 bits that combine to make an enum. Any suggestions?

WhittlesJr avatar Aug 16 '18 22:08 WhittlesJr

For question 2: This sounds like a job for compile-codec. You could use a :ubyte codec which reads/writes 8bit values, and then define two functions that convert a byte to the object you are refering to (containing 6 flags and an enum) as well as the inverse function that turns such an object into a byte. The you could use (compile-codec :ubyte byte->foo foo->byte). For example, refer to the implementation of the codec enum that uses exactly this pattern.

smee avatar Aug 17 '18 06:08 smee

For question 1: Yes, something employing the header codec is what I would use. The main idea is capturing the pattern: Use something in the stream to determine how the next thing should be parsed. The example you referred to looks interesting. The missing implementation for write-data needs to be added, but that seems straightforward (add a second function that implements the inverse of select-codec). I'm not familar with BACnet, but if you have a specific part you have trouble with I can try to have a look at it.

smee avatar Aug 17 '18 06:08 smee

Also, maybe something like the ID3v2 parser for mp3 tags might be useful as a reference. There is a rather large, hetereogeneous and growing number of ID3 tags, so maybe that usecase is similar to yours?

smee avatar Aug 17 '18 06:08 smee

Forgot the link: https://github.com/zsau/id3

smee avatar Aug 17 '18 06:08 smee

Thank you! I'll dig into it

WhittlesJr avatar Aug 17 '18 15:08 WhittlesJr

As for my second question (on complex bit handling), I've written a codec to generically handle this sort of case, like so:

Config:

[:contains-apdu nil :dest-present nil :source-present :reply-expected
 {:bits     2
  :name     :priority
  :enum-map {:life-safety        2r11
             :critical-equipment 2r10
             :urgent             2r01
             :normal             2r00}}]

Input: 0x25 Output:

{:contains-apdu  false,
 :dest-present   true,
 :source-present false,
 :reply-expected true,
 :priority       :urgent}

Ordering of bits vs enums doesn't matter. If no enum-map is given, it'll just return the sum numeric value of the bits.

Does that sound like something that could belong in binary-dsl? If so, would you like to take a look at a pull request and give me some feedback?

WhittlesJr avatar Aug 17 '18 17:08 WhittlesJr

And as for the codec selection in the header, do you think select-codec from the example I referenced looks like another candidate for inclusion in your library? (Assuming write-data was implemented). It seems like a pretty general-purpose solution to a common problem.

WhittlesJr avatar Aug 17 '18 19:08 WhittlesJr

I solved all of my problems! I have BACnet encoding and decoding working now.

I ended up implementing an additional generic codec which I think may be useful to others. Sometime soon I'll write up a gist so you can review and see if it's worth incorporating.

I'm also thinking about how to streamline the whole spec into pure EDN, which may also prove useful generally.

WhittlesJr avatar Aug 22 '18 22:08 WhittlesJr

That is great to hear! Will you publish the BACnet parser in the future? I would love to link to it as a more complex example.

Regarding your questions: I'm trying to keep the set of predefined codecs as small as possible. Your bit-codec points to a gap in our provided functionality: Codecs are primarily byte-based. Handling bits is not this libraries strongest suit. Maybe in the future we could support generic bit streams, then something like you suggested might fit right in. Also, something like Facebooks Gorilla compression scheme could be expressed, which is not possible right now.

smee avatar Aug 23 '18 05:08 smee

A pure data representation of codecs might be useful. I didn't have the need yet. Some codecs like header which use custom functions might be hard to represent. It would still need a mixed representation (some functions in a namespace and references in the EDN).

smee avatar Aug 23 '18 05:08 smee

So I really really want to publish my BACnet parser as open source, but as I wrote it for my employer, that may or may not happen. I'm going to keep pushing for it.

And yeah I've pretty much discarded the idea of pure-EDN specs. It's working quite well as is.

WhittlesJr avatar Mar 19 '19 14:03 WhittlesJr