Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cbor: CBOR implementation for RIOT-OS (SWP) #1415

Merged
merged 1 commit into from
Aug 5, 2014
Merged

Conversation

krf
Copy link
Contributor

@krf krf commented Jul 14, 2014

cbor: CBOR implementation for RIOT-OS                                                                                                                                                                  

This is a malloc-free implementation of the Concise Binary Object                                                                                                                                      
Representation (CBOR) data format for the RIOT-OS.                                                                                                                                                     

This implementation mostly stand-alone, and it should be pretty easy to                                                                                                                                
port to other platforms. We're only using the C STL and some custom                                                                                                                                    
network-related functionaliy which could be easily replaced by depending                                                                                                                               
on arpa/inet.h.                                                                                                                                                                                        

The CBOR API is straight-forward to use and provides encoding/decoding                                                                                                                                 
functionality for all major C types, such as:                                                                                                                                                          
- int                                                                                                                                                                                                  
- uint64_t                                                                                                                                                                                             
- int64_t                                                                                                                                                                                              
- float                                                                                                                                                                                                
- double                                                                                                                                                                                               
- char*                                                                                                                                                                                                
- struct tm                                                                                                                                                                                            
- time_t                                                                                                                                                                                               

It is possible to conditionally compile this module via CFLAGS:                                                                                                                                        
- CBOR_NO_CTIME: All ctime related features removed
- CBOR_NO_FLOAT: All floating-point related features removed
- CBOR_NO_STREAM_DECODE: Generic decoding feature removed
  (cbor_stream_decode normally prints out a CBOR data stream to stdout)

@krf
Copy link
Contributor Author

krf commented Jul 14, 2014

More squashing wasn't possible without destroying the logical order of the commits. We could still squash, but then we would end up with just a very few commits.

#include <stdlib.h>
#include <string.h>

#define CBOR_TYPE_MASK 0xE0 // top 3 bits
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use C style comments (/* … */).

@OlegHahm
Copy link
Member

Blacklist the failing unittests (with insufficient memory where it applies).

/**
* Convert float @p x to network format
*/
static float htonf(float x)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This operation does not seem safe for arbitrary ABIs. If a floating point number is passed and/or returned in special FPU registers, then the multiple representations for ±NaN might get broken. I'd use int32_t or char[4] to pass the values.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed that by using the appropriate uintXX_t types for input/output. Please check again.

@Kijewski
Copy link
Contributor

What I'd really like for this PR would be feature subsetting, e.g. that the user could decide not to include floating point support.

@Kijewski
Copy link
Contributor

Also your code does not need to be defensive. If the user uses the API wrong, then let the application crash mercilessly. We have the macro DEVELHELP that a user can set if defensive checks should be done:

#ifdef DEVELHELP
if (!s) {
    puts("cbor: s==NULL, your application will now crash"); 
    /* No return here. Let it crash. */
}
#endif

Input streams and buffers should still be tested, though, or the remote side could exploit the implementation.

@LudwigKnuepfer
Copy link
Member

Regarding squashing: I'd say it's OK if there's just one big 'initial import' commit (without looking at the actual change set..).
Edit: I'm still of that opinion after looking at the CS.

@krf
Copy link
Contributor Author

krf commented Jul 14, 2014

I've introduced the following macros to be able to conditionally leave out parts of the CBOR module:
* CBOR_NO_CTIME: All ctime related features removed
* CBOR_NO_FLOAT: All floating-point related features removed
* CBOR_NO_STREAM_DECODE: Generic decoding feature removed
(cbor_stream_decode normally prints out a CBOR data stream to stdout)

I've squashed the PR into a single commit and extended the commit message.

Regarding "not writing defensive code": Why is that bad? I really don't like code to be crashy just because we don't check user input.

@krf
Copy link
Contributor Author

krf commented Jul 14, 2014

@OlegHahm "Blacklist the failing unittests" <- I don't quite understand. Isn't the problem that we're packing too many code into the unittest executable? At least that's how I'm interpreting the error message when running BOARD=chronos make:

/usr/lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld: /home/krf/devel/src/RIOT/tests/unittests/bin/chronos/unittests.elf section `.rodata' will not fit in region `rom'
/usr/lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld: section .vectors loaded at [000000000000ff80,000000000000ffff] overlaps section .rodata loaded at [000000000000ec6c,0000000000010879]
/usr/lib/gcc/msp430/4.6.3/../../../../msp430/bin/ld: region `rom' overflowed by 2476 bytes

@Kijewski
Copy link
Contributor

Regarding "not writing defensive code": Why is that bad? I really don't like code to be crashy just because we don't check user input.

Normally defensive code is a good thing 👍, but not if you have a very limited ROM 👎 like some platforms we are targeting. Like you saw in this PR, the ROM for chronos is overflowing for the unittests. In such an environment you need to trust the programmer to write good code.

s->data[s->pos++] = major_type | val;
return 1;
}
else if (val <= 0xff) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could refactor everything after the trivial case to first find the length, and then use a loop. Something like (untested):

int result;

if (val <= 0xff) {
    major_type |= CBOR_UINT8_FOLLOWS;
    result = 2;
}
elseCBOR_ENSURE_SIZE(s, result);
s->data[s->pos++] = major_type;
for (int i = result - 1; i >= 0; --i) {
    s->data[s->pos++] = (val >> (8 * i)) & 0xff;
}

return result;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks for the hint.

This and my changes to decode_int reduces the code size in about 1K on native. Good.

@OlegHahm
Copy link
Member

@krf:

@OlegHahm "Blacklist the failing unittests" <- I don't quite understand. Isn't the problem that we're packing too many code into the unittest executable? At least that's how I'm interpreting the error message when running BOARD=chronos make

Yes, you're interpreting it right. Usually, if an example (or test) application doesn't fit into the RAM (or ROM), we add the board to a black list in the Makefile, e.g. BOARD_INSUFFICIENT_RAM := chronos (resp. BOARD_BLACKLIST := chronos). But in the case of the unittests it might indeed make more sense to split them up in a way, that you can compile them and run them for every platform.

@krf
Copy link
Contributor Author

krf commented Jul 15, 2014

we add the board to a black list in the Makefile, e.g. BOARD_INSUFFICIENT_RAM := chronos (resp. BOARD_BLACKLIST := chronos).

I can only add that to tests/unittests/Makefile. However, then the complete test suite won't be linked on that target. That's undesired, I guess. What can I do as a quick fix in order to get it past the CI?

@krf
Copy link
Contributor Author

krf commented Jul 22, 2014

Okay. New changeset:

  • Restructured the unit test code
  • Introduce CBOR_NO_SEMANTIC_TAGGING

What else can I do to get this merged? :)

@Kijewski
Copy link
Contributor

I see no show stoppers in here. Well done 👍

@OlegHahm
Copy link
Member

That's an ACK?

@krf
Copy link
Contributor Author

krf commented Jul 25, 2014

Squashed + rebased onto master another time. Let me know if there's anything left I can do.

@Kijewski
Copy link
Contributor

Debug printing must be disabled by default.
The unittest for chronos and redbee-econotag must be BOARD_INSUFFICIENT_RAM'd.

@Kijewski
Copy link
Contributor

Kijewski commented Aug 1, 2014

@krf, ping.

@krf
Copy link
Contributor Author

krf commented Aug 4, 2014

Updated PR.

Note: I'm not sure what is wrong with https://travis-ci.org/RIOT-OS/RIOT/builds/31483750. -- It also happens when running the 'compile_test.py' script for branch 'master'.

@OlegHahm
Copy link
Member

OlegHahm commented Aug 4, 2014

compile_test.py succeeds on my computer and Travis seems to pass, too - apart from a weird timeout at the end.

return bytes_follow + 1;
}

static size_t encode_bytes(unsigned char major_type, cbor_stream_t *s, const char *data, size_t length)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@krf
Copy link
Contributor Author

krf commented Aug 5, 2014

Issues fixed

@OlegHahm
Copy link
Member

OlegHahm commented Aug 5, 2014

As far as I can see the Travis error happens already in master - seems to be a problem with q386. @Kijewski, can you take a look? make -C ./tests/unittests test BOARD=qemu-i386 generates no sensible output:

`--> make -C ./tests/unittests test BOARD=qemu-i386                                                                                                                                                [10:13:11][0]
make: Entering directory '/home/oleg/git/RIOT/tests/unittests'
/home/oleg/git/RIOT/Makefile.include:140: target '/home/oleg/git/RIOT/tests/unittests/bin/qemu-i386/tlsf.a' given more than once in the same rule
Type 'exit' to exit.

"And Then There Was Silence" (guess the artist).

@Kijewski
Copy link
Contributor

Kijewski commented Aug 5, 2014

No, the build history is happy: https://travis-ci.org/RIOT-OS/RIOT/builds.
I cannot reproduce the error at some, but is reliable occurs in Travis. I don't see any problems in this PR, so a cannot say what the problem is.

@OlegHahm
Copy link
Member

OlegHahm commented Aug 5, 2014

Hm, so let's merge this?

@OlegHahm
Copy link
Member

OlegHahm commented Aug 5, 2014

Maybe you can squash the formatting commits.

@haukepetersen
Copy link
Contributor

I give an ACK when suqashed

This is a malloc-free implementation of the Concise Binary Object
Representation (CBOR) data format for the RIOT-OS.

This implementation mostly stand-alone, and it should be pretty easy to
port to other platforms. We're only using the C STL and some custom
network-related functionaliy which could be easily replaced by depending
on arpa/inet.h.

The CBOR API is straight-forward to use and provides encoding/decoding
functionality for all major C types, such as:
- int
- uint64_t
- int64_t
- float
- double
- char*
- struct tm
- time_t

It is possible to conditionally compile this module via CFLAGS:
- CBOR_NO_SEMANTIC_TAGGING: All semantic-tagging features removed
- CBOR_NO_CTIME: All ctime related features removed
- CBOR_NO_FLOAT: All floating-point related features removed
- CBOR_NO_PRINT: All features depending on printf removed
@krf
Copy link
Contributor Author

krf commented Aug 5, 2014

Squashed.

@OlegHahm
Copy link
Member

OlegHahm commented Aug 5, 2014

Hooray, we have CBOR! Nice Achievement, @krf and @Jane333!

OlegHahm added a commit that referenced this pull request Aug 5, 2014
cbor: CBOR implementation for RIOT-OS (SWP)
@OlegHahm OlegHahm merged commit 6c2b2ce into RIOT-OS:master Aug 5, 2014
@Kijewski
Copy link
Contributor

Kijewski commented Aug 6, 2014

@OlegHahm, you merged this even though there were outstanding Travis errors. Every build since then fails.

@miri64
Copy link
Member

miri64 commented Aug 6, 2014

Since this is a Travis problem (at least to according to the discussion above) and not a problem with this PR, I think this is alright.

@Kijewski
Copy link
Contributor

Kijewski commented Aug 6, 2014

I cannot really investigate the problem, as I failed to get Travis to email me the compiled .elf file.

@OlegHahm
Copy link
Member

OlegHahm commented Aug 6, 2014

What @authmillenon says. make -C ./tests/unittests test BOARD=qemu-i386 was failing on master before and after the merge.

@Kijewski
Copy link
Contributor

Kijewski commented Aug 6, 2014

But it wasn't. https://travis-ci.org/RIOT-OS/RIOT/builds

@OlegHahm
Copy link
Member

OlegHahm commented Aug 6, 2014

I checked locally. Currently I'm not trusting Travis too much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: network Area: Networking Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants