cbor-simple 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f0cf7690f1fb741f8a95075b8a197168704fd2bf
4
+ data.tar.gz: 2c5d4756029677dd6ee6e4601b4d059305205242
5
+ SHA512:
6
+ metadata.gz: 1e2a40952af4869b675e83ee68b42d5a2e82ec1ce30b9eb6d1f6dd4520e15ba98a361f5ec94553c97a00f5e2af63c7b835b848c361c4244d767980c9ae90ab44
7
+ data.tar.gz: 99542bf58913f7fa82b626a0d752d5c805e23f90545da64a1f63dfaef4c6cecc2a1363c3165d92d74a7a4de916fc18ba0028825d14379eee35b63aade9514484
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cbor-simple.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Lucas Clemente
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # CBOR Simple
2
+
3
+ ```ruby
4
+ gem 'cbor-simple'
5
+ ```
6
+
7
+ A basic but extensible implementation of [CBOR (RFC7049)](http://tools.ietf.org/html/rfc7049) in plain ruby.
8
+
9
+ Use just like `JSON` or `YAML`:
10
+
11
+ ```ruby
12
+ CBOR.dump(42) # => "\x18\x2a"
13
+ CBOR.load("\x18\x2a") # => 42
14
+ ```
15
+
16
+ You can add custom tags like this:
17
+
18
+ ```ruby
19
+ CBOR.register_tag 0 do
20
+ Time.iso8601(load())
21
+ end
22
+ ```
23
+
24
+ And add classes for dumping:
25
+
26
+ ```ruby
27
+ CBOR.register_class Time, 0 do |obj|
28
+ dump(obj.iso8601(6))
29
+ end
30
+ ```
31
+
32
+ Custom tags can also be given as second parameter to `load`, however this should be considered unstable and might change in the future.
33
+
34
+ ```ruby
35
+ # Invert values tagged with 0x26 (nonstandard!)
36
+ CBOR.load("\xd8\x26\xf5", {0x26 => ->{ !load }}) # => false
37
+ ```
38
+
39
+ Currently supported classes:
40
+
41
+ - (Unsigned) Integers
42
+ - Floats (single and double)
43
+ - Byte / Textstring (also symbols)
44
+ - Arrays
45
+ - Hashes
46
+ - BigDecimals
47
+ - UUIDs (if the gem `uuidtools` is visible)
48
+ - Times
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/*_test.rb"
6
+ t.libs.push "test"
7
+ end
8
+
9
+ task default: :test
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cbor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cbor-simple"
8
+ spec.version = CBOR::VERSION
9
+ spec.authors = ["Lucas Clemente"]
10
+ spec.email = ["lucas@clemente.io"]
11
+ spec.summary = %q{Basic but extensible CBOR implementation for ruby.}
12
+ spec.homepage = "https://github.com/lucas-clemente/cbor-simple"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "minitest"
23
+ spec.add_development_dependency "minitest-emoji"
24
+ spec.add_development_dependency "uuidtools"
25
+ end
data/lib/cbor.rb ADDED
@@ -0,0 +1,68 @@
1
+ require "bigdecimal"
2
+ require "stringio"
3
+
4
+ require "cbor/version"
5
+ require "cbor/consts"
6
+ require "cbor/error"
7
+ require "cbor/dumper"
8
+ require "cbor/loader"
9
+
10
+ module CBOR
11
+ def self.dump(val)
12
+ Dumper.new.dump(val)
13
+ end
14
+
15
+ # Load CBOR-encoded data.
16
+ #
17
+ # `binary` can be either a String or an IO object. In both cases the first
18
+ # encoded object will be returned.
19
+ #
20
+ # Local tags (as a hash of tag => lambda) will only be valid within this method.
21
+ def self.load(binary, local_tags = {})
22
+ if binary.is_a? String
23
+ binary = StringIO.new(binary)
24
+ elsif !binary.is_a? IO
25
+ raise CborError.new("can only load from String or IO")
26
+ end
27
+ Loader.new(binary, local_tags).load
28
+ end
29
+
30
+ def self.register_class(klass, tag, &block)
31
+ Dumper.register_class(klass, tag, &block)
32
+ end
33
+
34
+ def self.register_tag(tag, &block)
35
+ Loader.register_tag(tag, &block)
36
+ end
37
+ end
38
+
39
+ CBOR.register_class Time, CBOR::Consts::Tag::DATETIME do |val|
40
+ dump(val.iso8601(6))
41
+ end
42
+
43
+ CBOR.register_tag CBOR::Consts::Tag::DATETIME do
44
+ Time.iso8601(load)
45
+ end
46
+
47
+ CBOR.register_class BigDecimal, CBOR::Consts::Tag::DECIMAL do |val|
48
+ sign, significant_digits, base, exponent = val.split
49
+ raise CBOR::CborError.new("NaN while sending BigDecimal #{val.inspect}") if sign == 0
50
+ val = sign * significant_digits.to_i(base)
51
+ dump([exponent - significant_digits.size, val])
52
+ end
53
+
54
+ CBOR.register_tag CBOR::Consts::Tag::DECIMAL do
55
+ arr = load
56
+ raise CBOR::CborError.new("invalid decimal") if arr.length != 2
57
+ BigDecimal.new(arr[1]) * (BigDecimal.new(10) ** arr[0])
58
+ end
59
+
60
+ if defined? UUIDTools::UUID
61
+ CBOR.register_class UUIDTools::UUID, CBOR::Consts::Tag::UUID do |val|
62
+ dump val.raw.b
63
+ end
64
+
65
+ CBOR.register_tag CBOR::Consts::Tag::UUID do
66
+ UUIDTools::UUID.parse_raw(load)
67
+ end
68
+ end
@@ -0,0 +1,27 @@
1
+ module CBOR::Consts
2
+ module Major
3
+ UINT = 0
4
+ NEGINT = 1
5
+ BYTESTRING = 2
6
+ TEXTSTRING = 3
7
+ ARRAY = 4
8
+ MAP = 5
9
+ TAG = 6
10
+ SIMPLE = 7
11
+ end
12
+
13
+ module Tag
14
+ DATETIME = 0
15
+ DECIMAL = 4
16
+ UUID = 37
17
+ end
18
+
19
+ module Simple
20
+ FALSE = 20
21
+ TRUE = 21
22
+ NULL = 22
23
+ FLOAT16 = 25
24
+ FLOAT32 = 26
25
+ FLOAT64 = 27
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ class CBOR::Dumper
2
+ include CBOR::Consts
3
+
4
+ @@registered_classes = {}
5
+
6
+ def self.register_class(klass, tag, &block)
7
+ @@registered_classes[klass] = {
8
+ tag: tag,
9
+ block: block
10
+ }
11
+ end
12
+
13
+ def dump(val)
14
+ case val
15
+ when nil
16
+ dump_simple(Simple::NULL)
17
+ when TrueClass
18
+ dump_simple(Simple::TRUE)
19
+ when FalseClass
20
+ dump_simple(Simple::FALSE)
21
+ when Fixnum, Bignum
22
+ if val < 0
23
+ dump_uint(Major::NEGINT, -val-1)
24
+ else
25
+ dump_uint(Major::UINT, val)
26
+ end
27
+ when Float
28
+ dump_simple(Simple::FLOAT64) + [val].pack("G")
29
+ when String, Symbol
30
+ s = val.to_s.dup.force_encoding(Encoding::UTF_8)
31
+ dump_uint(s.valid_encoding? ? Major::TEXTSTRING : Major::BYTESTRING, s.bytesize) + s.b
32
+ when Array
33
+ dump_uint(Major::ARRAY, val.count) + val.map{|e| dump(e)}.join
34
+ when Hash
35
+ dump_uint(Major::MAP, val.count) + val.map{|k, v| dump(k.to_s) + dump(v)}.join
36
+ else
37
+ if h = @@registered_classes[val.class]
38
+ dump_uint(Major::TAG, h[:tag]) + instance_exec(val, &h[:block])
39
+ else
40
+ raise CBOR::CborError.new("dumping not supported for objects of type #{val.class} (#{val.inspect})")
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def dump_uint(major, val)
48
+ typeInt = (major << 5);
49
+ if val <= 23
50
+ [typeInt | val].pack("C")
51
+ elsif val < 0x100
52
+ [typeInt | 24, val].pack("CC")
53
+ elsif val < 0x10000
54
+ [typeInt | 25, val].pack("CS>")
55
+ elsif val < 0x100000000
56
+ [typeInt | 26, val].pack("CL>")
57
+ elsif val < 0x10000000000000000
58
+ [typeInt | 27, val].pack("CQ>")
59
+ else
60
+ raise CBOR::CborError.new("this cbor implementation only supports integers up to 64bit")
61
+ end
62
+ end
63
+
64
+ def dump_simple(val)
65
+ [(Major::SIMPLE << 5) | val].pack("C")
66
+ end
67
+ end
data/lib/cbor/error.rb ADDED
@@ -0,0 +1,3 @@
1
+ module CBOR
2
+ class CborError < RuntimeError; end
3
+ end
@@ -0,0 +1,94 @@
1
+ class CBOR::Loader
2
+ include CBOR::Consts
3
+
4
+ @@registered_tags = {}
5
+
6
+ def self.register_tag(tag, &block)
7
+ @@registered_tags[tag] = block
8
+ end
9
+
10
+ def initialize(io, local_tags)
11
+ @io = io
12
+ @local_tags = local_tags
13
+ end
14
+
15
+ def load
16
+ typeInt = get_bytes(1, "C")
17
+ major = (typeInt >> 5)
18
+
19
+ case major
20
+ when Major::UINT
21
+ get_uint(typeInt)
22
+ when Major::NEGINT
23
+ -get_uint(typeInt)-1
24
+ when Major::BYTESTRING
25
+ count = get_uint(typeInt)
26
+ get_bytes(count, "a*")
27
+ when Major::TEXTSTRING
28
+ count = get_uint(typeInt)
29
+ s = get_bytes(count, "a*").force_encoding(Encoding::UTF_8)
30
+ unless s.valid_encoding?
31
+ raise CBOR::CborError.new("Received non-utf8 string when expecting utf8")
32
+ end
33
+ s
34
+ when Major::ARRAY
35
+ count = get_uint(typeInt)
36
+ count.times.map { load }
37
+ when Major::MAP
38
+ count = get_uint(typeInt)
39
+ Hash[count.times.map { [load, load] }]
40
+ when Major::TAG
41
+ tag = get_uint(typeInt)
42
+ if block = @@registered_tags[tag]
43
+ instance_exec(&block)
44
+ elsif block = @local_tags[tag]
45
+ instance_exec(&block)
46
+ else
47
+ raise CBOR::CborError.new("Unknown tag #{tag}")
48
+ end
49
+ when Major::SIMPLE
50
+ case typeInt & 0x1F
51
+ when Simple::FALSE
52
+ false
53
+ when Simple::TRUE
54
+ true
55
+ when Simple::NULL
56
+ nil
57
+ when Simple::FLOAT32
58
+ get_bytes(4, "g")
59
+ when Simple::FLOAT64
60
+ get_bytes(8, "G")
61
+ else
62
+ raise CBOR::CborError.new("Unknown simple type #{simple}")
63
+ end
64
+ else
65
+ raise CBOR::CborError.new("Unknown major type #{major}")
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def get_uint(typeInt)
72
+ smallLen = typeInt & 0x1F
73
+ case smallLen
74
+ when 24
75
+ get_bytes(1, "C")
76
+ when 25
77
+ get_bytes(2, "S>")
78
+ when 26
79
+ get_bytes(4, "L>")
80
+ when 27
81
+ get_bytes(8, "Q>")
82
+ else
83
+ smallLen
84
+ end
85
+ end
86
+
87
+ def get_bytes(count, pack_opts)
88
+ s = @io.read(count)
89
+ if s.size < count
90
+ raise CBOR::CborError.new("Unexpected end of input")
91
+ end
92
+ s.unpack(pack_opts).first
93
+ end
94
+ end
@@ -0,0 +1,3 @@
1
+ module CBOR
2
+ VERSION = "1.0.0"
3
+ end
data/test/data.rb ADDED
@@ -0,0 +1,25 @@
1
+ TEST_DATA = {
2
+ "\xf6".b => nil,
3
+ "\xf5".b => true,
4
+ "\xf4".b => false,
5
+ "\x01".b => 1,
6
+ "\x18\x2a".b => 42,
7
+ "\x18\x64".b => 100,
8
+ "\x1a\x00\x0f\x42\x40".b => 1000000,
9
+ "\x1b\x00\x00\x00\xe8\xd4\xa5\x10\x00".b => 1000000000000,
10
+ "\x20".b => -1,
11
+ "\x38\x63".b => -100,
12
+ "\x39\x03\xe7".b => -1000,
13
+ "\x3b\x00\x00\x00\xe8\xd4\xa5\x0f\xff".b => -1000000000000,
14
+ "\xfb\x3f\xf1\x99\x99\x99\x99\x99\x9a".b => 1.1,
15
+ "\xfb\x7e\x37\xe4\x3c\x88\x00\x75\x9c".b => 1.0e+300,
16
+ "\x42\xc3\x28".b => "\xc3\x28".b,
17
+ "\x66foobar".b => "foobar",
18
+ "\x67f\xc3\xb6obar".b => "föobar",
19
+ "\x82\x63foo\x63bar".b => ["foo", "bar"],
20
+ "\xa2\x63foo\x01\x63bar\x02".b => {"foo" => 1, "bar" => 2},
21
+ "\xc0\x78\x1b\x32\x30\x31\x33\x2d\x30\x33\x2d\x32\x31\x54\x32\x30\x3a\x30\x34\x3a\x30\x30.000000\x5a".b => Time.iso8601("2013-03-21T20:04:00Z"),
22
+ "\xc0\x78\x1b\x32\x30\x31\x33\x2d\x30\x33\x2d\x32\x31\x54\x32\x30\x3a\x30\x34\x3a\x30\x30.000001\x5a".b => Time.iso8601("2013-03-21T20:04:00.000001Z"),
23
+ "\xc4\x82\x21\x19\x6a\xb3".b => BigDecimal.new("273.15"),
24
+ "\xd8\x25\x50\xD5\x06\x81\xAE\xB2\xC1\x49\x4B\xB2\x6E\x7F\xA4\xF7\xEE\x61\x37".b => UUIDTools::UUID.parse("D50681AE-B2C1-494B-B26E-7FA4F7EE6137"),
25
+ }
data/test/dump_test.rb ADDED
@@ -0,0 +1,27 @@
1
+ require "test_helper"
2
+ require "data"
3
+
4
+ class TestDump < Minitest::Test
5
+ def test_dumping
6
+ TEST_DATA.each do |bin, val|
7
+ assert_equal bin, CBOR.dump(val), "dumping #{val.inspect}"
8
+ end
9
+ end
10
+
11
+ class Dummy; end
12
+ def test_dump_invalid
13
+ assert_raises CBOR::CborError do
14
+ CBOR.dump(Dummy.new)
15
+ end
16
+ end
17
+
18
+ def test_dump_large_int
19
+ assert_raises CBOR::CborError do
20
+ CBOR.dump(18446744073709551616)
21
+ end
22
+ end
23
+
24
+ def test_dumps_symbols
25
+ assert_equal "\x66foobar".b, CBOR.dump(:foobar)
26
+ end
27
+ end
data/test/load_test.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "test_helper"
2
+
3
+ class TestLoad < Minitest::Test
4
+ def test_loading
5
+ TEST_DATA.each do |bin, val|
6
+ assert_equal val, CBOR.load(bin), "loading #{val.inspect}"
7
+ end
8
+ end
9
+
10
+ def test_loading_from_junk
11
+ assert_raises CBOR::CborError do
12
+ CBOR.load(5)
13
+ end
14
+ end
15
+
16
+ def test_loading_local_tags
17
+ assert_equal false, CBOR.load("\xd8\x26\xf5", {0x26 => ->{ !load }})
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ require "minitest/autorun"
2
+ require "minitest/emoji"
3
+ require "uuidtools"
4
+ require "cbor"
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cbor-simple
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Lucas Clemente
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-emoji
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: uuidtools
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - lucas@clemente.io
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - cbor-simple.gemspec
96
+ - lib/cbor.rb
97
+ - lib/cbor/consts.rb
98
+ - lib/cbor/dumper.rb
99
+ - lib/cbor/error.rb
100
+ - lib/cbor/loader.rb
101
+ - lib/cbor/version.rb
102
+ - test/data.rb
103
+ - test/dump_test.rb
104
+ - test/load_test.rb
105
+ - test/test_helper.rb
106
+ homepage: https://github.com/lucas-clemente/cbor-simple
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.2.2
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Basic but extensible CBOR implementation for ruby.
130
+ test_files:
131
+ - test/data.rb
132
+ - test/dump_test.rb
133
+ - test/load_test.rb
134
+ - test/test_helper.rb