├── .gitignore ├── .travis.yml ├── ChangeLog ├── Gemfile ├── README.rdoc ├── Rakefile ├── cbor.gemspec ├── doclib ├── cbor.rb └── cbor │ ├── buffer.rb │ ├── core_ext.rb │ ├── error.rb │ ├── packer.rb │ ├── simple.rb │ ├── tagged.rb │ └── unpacker.rb ├── ext └── cbor │ ├── buffer.c │ ├── buffer.h │ ├── buffer_class.c │ ├── buffer_class.h │ ├── cbor.h │ ├── compat.h │ ├── core_ext.c │ ├── core_ext.h │ ├── extconf.rb │ ├── packer.c │ ├── packer.h │ ├── packer_class.c │ ├── packer_class.h │ ├── rbinit.c │ ├── renamer.h │ ├── rmem.c │ ├── rmem.h │ ├── sysdep.h │ ├── sysdep_endian.h │ ├── sysdep_types.h │ ├── unpacker.c │ ├── unpacker.h │ ├── unpacker_class.c │ └── unpacker_class.h ├── lib ├── cbor.rb └── cbor │ └── version.rb └── spec ├── buffer_io_spec.rb ├── buffer_spec.rb ├── cases.cbor ├── cases.cbor_stream ├── cases.json ├── cases.msg ├── cases_compact.msg ├── cases_spec.rb ├── format_spec.rb ├── packer_spec.rb ├── random_compat.rb ├── spec_helper.rb └── unpacker_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.gem 4 | *.class 5 | .bundle 6 | Gemfile.lock 7 | pkg 8 | test/debug.log 9 | *~ 10 | /rdoc 11 | tmp 12 | .classpath 13 | .project 14 | .settings 15 | /nbproject/private/ 16 | ext/java/build 17 | ext/java/msgpack.jar 18 | spec/bench* 19 | lib/cbor/cbor.bundle 20 | TAGS 21 | .yardoc 22 | doc 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 2.1.10 5 | - 2.2.10 6 | - 2.3.8 7 | - 2.4.6 8 | - 2.5.5 9 | - 2.6.3 10 | - ruby-head 11 | matrix: 12 | allow_failures: 13 | - rvm: 2.0.0 14 | - rvm: 2.1.10 15 | - rvm: 2.2.10 16 | - rvm: ruby-head 17 | 18 | gemfile: 19 | - Gemfile 20 | 21 | script: "bundle exec rake spec" 22 | 23 | notifications: 24 | irc: "irc.freenode.org#coap" 25 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2016-04-11 Carsten Bormann 2 | 3 | * 0.5.9.1: Fix WIN32 problem inherited from upstream 4 | 5 | 2014-12-13 Carsten Bormann 6 | 7 | * 0.5.9.0: Upstream fixes; prepare for Ruby 2.2 (use GC-able 8 | symbols for :keys_as_symbols, use rb_integer_pack if available) 9 | 10 | 2014-12-12 Carsten Bormann 11 | 12 | * 0.5.8.0: Upstream fixes (including {symbolize_keys: true} as an 13 | alternative for :keys_as_symbols); README updates. 14 | 15 | 2014-02-09 Carsten Bormann 16 | 17 | * 0.5.6.4: Add more checking for bare indefinite breaks and 18 | non-strings inside indefinite strings. 19 | 20 | 2013-08-17 Carsten Bormann 21 | 22 | * First version of CBOR variant of this code. 23 | 24 | 2013-09-23 version 0.5.6: 25 | 26 | * Fixed "can't modify frozen String" exception in Unpacker with ruby 2.1.0-dev 27 | * Getting work with Ruby v2.0 on Windows (Thank you @thegreendroid) 28 | * Fixed EOFError handling in Unpacker 29 | 30 | 31 | 2013-05-12 version 0.5.5: 32 | 33 | * Fixed SEGV problem in to_msgpack 34 | * Fixed a possible race condition in MessagePack.load when it loads data from IO 35 | * mingw32 package includes binary for ruby-2.0.0 36 | 37 | 38 | 2013-03-15 version 0.5.4: 39 | 40 | * Added missing MessagePack::Unpacker#reset method 41 | 42 | 43 | 2013-02-14 version 0.5.3: 44 | 45 | * Fixed segfault problem on Buffer#clear (reuse rmem internal fragment optimization) 46 | * Fixed segfault problem on Buffer (rmem free code) 47 | 48 | 49 | 2013-02-07 version 0.5.2: 50 | 51 | * Fixed invalid pack/unpack on 32bit architecture such as Win32 52 | * Disable rmem on Rubinius because rmem is not thread safe 53 | 54 | 55 | 2012-12-23 version 0.5.1: 56 | 57 | * Fixed compile error with Rubinius 2.0.0-dev 58 | * Optimized msgpack_packer_write_hash for Rubinius 59 | 60 | 61 | 2012-12-20 version 0.5.0: 62 | 63 | * Rewrote all code and improved performance significantly 64 | * Added MessagePack::Buffer class 65 | * Added MessagePack::Packer class 66 | * Added Packer#buffer and Unpacker#buffer accessors which return MessagePack::Buffer 67 | * Added Packer#write_{array,map}_header and Unpacker#read_{array,map}_header methods 68 | * Added Packer#write_nil and Unpacker#skip_nil methods 69 | * Added Packer#write -> #pack alias and Unpacker#read method 70 | * Added exception classes - UnpackError, MalformedFormatError, StackError and TypeError 71 | * Added MessagePack.dup -> .pack and MessagePack.load -> .unpack aliases 72 | * Added Packer#empty?, #size and #clear methods 73 | * Added Packer#write_to(io) method to flush serialized data to IO efficiently 74 | * Added Unpacker#skip method to skip an object efficiently 75 | * Removed obsoleted Unpacker#fill, #execute, #execute_limit, #finished? and #data methods 76 | * Removed obsoleted Unapcker#stream and #stream= methods. Use unpacker.buffer.io instead 77 | 78 | 79 | 2012-05-05 version 0.4.7: 80 | 81 | * Fixed serialization of double values on ARM OABI architectures 82 | * Fixed byteorder problem on big-endian platforms 83 | * Don't use MRI internals in the Ruby extension for Rubinius 84 | * Detect whether st.h is present and don't use RUBY_VM as the condition for 85 | Rubinius 86 | 87 | 2011-08-08 version 0.4.6: 88 | 89 | * Fixed compile error problem on Mac OS X Lion 90 | 91 | 2011-05-09 version 0.4.5: 92 | 93 | * Improves compatibility with JRuby 94 | 95 | 2010-11-28 version 0.4.4: 96 | 97 | * Adds Unpacker#feed_each method 98 | * Improves compatibility with Rubinius 99 | * Improves compatibility with ruby-1.8.5 100 | * Encodings of String instances to UTF-8 on Ruby 1.9 101 | 102 | 2010-06-29 version 0.4.3: 103 | 104 | * Adds MessagePack::VERSION constant 105 | * Fixes SEGV problem caused by GC bug at MessagePack_Unpacker_mark 106 | 107 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org/' 2 | 3 | gemspec 4 | 5 | group :test do 6 | gem "rake" 7 | gem 'rake-compiler' 8 | gem 'rspec' 9 | gem 'json' 10 | gem 'yard' 11 | end 12 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = CBOR for Ruby 2 | 3 | This is a Ruby implementation of the CBOR[http://cbor.io] encoding, based on 4 | the (polished) high-performance msgpack-ruby code. 5 | 6 | Documentation will follow, but generally, if you replace MessagePack 7 | and msgpack with CBOR and cbor in the text cited from MessagePack 8 | below, you will be on the right track. For a starter: 9 | 10 | require 'cbor' 11 | s = [1, 2, 33.5, 4].to_cbor #=> "\x84\x01\x02\xF9P0\x04" 12 | CBOR.decode(s) #=> [1, 2, 33.5, 4] 13 | 14 | Use RubyGems to install: 15 | 16 | gem install cbor 17 | 18 | CBOR is an object representation format defined by the IETF[http://ietf.org]. 19 | The specification[http://tools.ietf.org/html/rfc8949] 20 | is an IETF Standards-Track specification 21 | and has been published as RFC 8949 (superseding the older RFC 7049). 22 | 23 | This is all based on wonderful work by frsyuki, and I have no idea how 24 | to acknowledge him appropriately. This gem is not intended to fork or 25 | supersede MessagePack[http://msgpack.org], which has a vibrant 26 | ecosystem. It is just making use of the high-quality code that is 27 | available in this community. If MessagePack works for you, go for it. 28 | If you need CBOR, you need this. 29 | 30 | Todos: 31 | 32 | * This code has not yet been fully optimized, so it is still a few 33 | percent slower than msgpack-ruby. 34 | 35 | * Properly document things, in particular the classes CBOR::Simple and 36 | CBOR::Tagged. If you check out the source, you can +rake+ +doc+ to 37 | get some documentation in the directory +doc+ (see +index.html+ there). 38 | 39 | * Cover more rubies. 40 | * \[✔✔✔✔] tested on MRI (1.9.3, 2.0.0, 2.1.10, 2.2.10, 2.3.7, 2.4.4 and 2.5.1). 41 | * (\[✔] There now also is some basic MRI 1.8.7 compatibility, however 1.8.7 does not support differentiation between byte and text strings.) 42 | * \[✔] tested on Rubinius 2.4.1. 43 | * \[_] Publish the pure-ruby version and make it work the same way on JRuby. 44 | 45 | * Find and implement good ways to offer CBOR's indefinite length 46 | ("streaming") capability at the Ruby API level. (Decoding is fully 47 | supported, just no streaming or indefinite length encoding.) 48 | 49 | * Rename some of the internals from msgpack to cbor. Right now, much 50 | of the code still uses the name msgpack in its identifiers, to 51 | facilitate merging upstream fixes. (The msgpack and cbor gems 52 | coexist nicely in one MRI instance due to the magic in +renamer.h+.) 53 | 54 | Same Apache 2.0 License applies to the changes as to the original. 55 | For the changes: 56 | 57 | Author:: Carsten Bormann 58 | Copyright:: Copyright (c) 2013, 2014 Carsten Bormann 59 | License:: Apache License, Version 2.0 60 | 61 | {}[https://travis-ci.org/cabo/cbor-ruby] {Gem Version}[http://badge.fury.io/rb/cbor] 62 | 63 | For the original, see below. 64 | 65 | = MessagePack 66 | 67 | MessagePack[http://msgpack.org] is an efficient binary serialization format. 68 | It lets you exchange data among multiple languages like JSON but it's faster and smaller. 69 | For example, small integers (like flags or error code) are encoded into a single byte, 70 | and typical short strings only require an extra byte in addition to the strings themselves. 71 | 72 | If you ever wished to use JSON for convenience (storing an image with metadata) but could 73 | not for technical reasons (binary data, size, speed...), MessagePack is a perfect replacement. 74 | 75 | require 'msgpack' 76 | msg = [1,2,3].to_msgpack #=> "\x93\x01\x02\x03" 77 | MessagePack.unpack(msg) #=> [1,2,3] 78 | 79 | Use RubyGems to install: 80 | 81 | gem install msgpack 82 | 83 | or build msgpack-ruby and install: 84 | 85 | bundle 86 | rake 87 | gem install --local pkg/msgpack 88 | 89 | 90 | = Use cases 91 | 92 | * Store objects efficiently serialized by msgpack on memcached or Redis 93 | * In fact Redis supports msgpack in EVAL-scripts[http://redis.io/commands/eval] 94 | * Upload data in efficient format from mobile devices such as smartphones 95 | * MessagePack works on iPhone/iPad and Android. See also Objective-C[https://github.com/msgpack/msgpack-objectivec] and Java[https://github.com/msgpack/msgpack-java] implementations 96 | * Design a portable protocol to communicate with embedded devices 97 | * Check also Fluentd[http://fluentd.org/] which is a log collector which uses msgpack for the log format (they say it uses JSON but actually it's msgpack, which is compatible with JSON) 98 | * Exchange objects between software components written in different languages 99 | * You'll need a flexible but efficient format so that components exchange objects while keeping compatibility 100 | 101 | 102 | = Portability 103 | 104 | MessagePack for Ruby should run on x86, ARM, PowerPC, SPARC and other CPU architectures. 105 | 106 | And it works with MRI (CRuby) and Rubinius. 107 | Patches to improve portability is highly welcomed. 108 | 109 | 110 | = Serializing objects 111 | 112 | Use *MessagePack.pack* or *to_msgpack*: 113 | 114 | require 'msgpack' 115 | msg = MessagePack.pack(obj) # or 116 | msg = obj.to_msgpack 117 | 118 | == Streaming serialization 119 | 120 | Packer provides advanced API to serialize objects in streaming style: 121 | 122 | # serialize a 2-element array [e1, e2] 123 | pk = MessagePack::Packer.new(io) 124 | pk.write_array_header(2).write(e1).write(e2).flush 125 | 126 | See {API reference}[http://ruby.msgpack.org/MessagePack/Packer.html] for details. 127 | 128 | = Deserializing objects 129 | 130 | Use *MessagePack.unpack*: 131 | 132 | require 'msgpack' 133 | obj = MessagePack.unpack(msg) 134 | 135 | == Streaming deserialization 136 | 137 | Unpacker provides advanced API to deserialize objects in streaming style: 138 | 139 | # deserialize objects from an IO 140 | u = MessagePack::Unpacker.new(io) 141 | u.each do |obj| 142 | # ... 143 | end 144 | 145 | or event-driven style which works well with EventMachine: 146 | 147 | # event-driven deserialization 148 | def on_read(data) 149 | @u ||= MessagePack::Unpacker.new 150 | @u.feed_each(data) {|obj| 151 | # ... 152 | } 153 | end 154 | 155 | See {API reference}[http://ruby.msgpack.org/MessagePack/Unpacker.html] for details. 156 | 157 | 158 | = Buffer API 159 | 160 | MessagePack for Ruby provides a buffer API so that you can read or write data by hand, not via Packer or Unpacker API. 161 | 162 | This {MessagePack::Buffer}[http://ruby.msgpack.org/MessagePack/Buffer.html] is backed with a fixed-length shared memory pool which is very fast for small data (<= 4KB), 163 | and has zero-copy capability which significantly affects performance to handle large binary data. 164 | 165 | = How to build and run tests 166 | 167 | Before building msgpack, you need to install bundler and dependencies. 168 | 169 | gem install bundler 170 | bundle install 171 | 172 | Then, you can run the tasks as follows: 173 | 174 | * Build 175 | 176 | bundle exec rake build 177 | 178 | * Run tests 179 | 180 | bundle exec rake spec 181 | 182 | * Generating docs 183 | 184 | bundle exec rake doc 185 | 186 | = Copyright 187 | 188 | Author:: Sadayuki Furuhashi 189 | Copyright:: Copyright (c) 2008-2013 Sadayuki Furuhashi 190 | License:: Apache License, Version 2.0 191 | 192 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | require 'bundler' 3 | Bundler::GemHelper.install_tasks 4 | 5 | require 'fileutils' 6 | 7 | require 'rspec/core' 8 | require 'rspec/core/rake_task' 9 | require 'yard' 10 | 11 | RSpec::Core::RakeTask.new(:spec) do |t| 12 | t.rspec_opts = ["-c", "-f documentation"] 13 | t.rspec_opts << "-Ilib" 14 | t.pattern = 'spec/**/*_spec.rb' 15 | t.verbose = true 16 | end 17 | 18 | task :spec => :compile 19 | 20 | desc 'Run RSpec code examples and measure coverage' 21 | task :coverage do |t| 22 | ENV['SIMPLE_COV'] = '1' 23 | Rake::Task["spec"].invoke 24 | end 25 | 26 | desc 'Generate YARD document' 27 | YARD::Rake::YardocTask.new(:doc) do |t| 28 | t.files = ['lib/cbor/version.rb','doclib/**/*.rb'] 29 | t.options = [] 30 | t.options << '--debug' << '--verbose' if $trace 31 | end 32 | 33 | spec = eval File.read("cbor.gemspec") 34 | 35 | if RUBY_PLATFORM =~ /java/ 36 | require 'rake/javaextensiontask' 37 | 38 | Rake::JavaExtensionTask.new('cbor', spec) do |ext| 39 | ext.ext_dir = 'ext/java' 40 | #jruby_home = RbConfig::CONFIG['prefix'] 41 | #jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar'] 42 | #ext.classpath = jars.map { |x| File.expand_path x }.join ':' 43 | end 44 | 45 | else 46 | require 'rake/extensiontask' 47 | 48 | Rake::ExtensionTask.new('cbor', spec) do |ext| 49 | ext.cross_compile = true 50 | ext.lib_dir = File.join(*['lib', 'cbor', ENV['FAT_DIR']].compact) 51 | #ext.cross_platform = 'i386-mswin32' 52 | end 53 | end 54 | 55 | CLEAN.include('lib/cbor/*.jar') 56 | CLEAN.include('lib/cbor/cbor.*') 57 | 58 | task :default => :build 59 | 60 | 61 | ### 62 | ## Cross compile memo 63 | ## 64 | ## Ubuntu Ubuntu 10.04.1 LTS 65 | ## 66 | # 67 | ### install mingw32 cross compiler with w64 support 68 | # sudo apt-get install gcc-mingw32 69 | # sudo apt-get install mingw-w64 70 | # 71 | ### install rbenv 72 | # git clone git://github.com/sstephenson/rbenv.git ~/.rbenv 73 | # echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile 74 | # echo 'eval "$(rbenv init -)"' >> ~/.bash_profile 75 | # exec $SHELL -l 76 | # 77 | ### install cross-compiled ruby 2.0.0 78 | # rbenv install 2.0.0-p0 79 | # gem install rake-compiler 80 | # rake-compiler cross-ruby VERSION=2.0.0-p0 81 | # 82 | ### install cross-compiled ruby 1.9.3 83 | # rbenv install 1.9.3-p327 84 | # gem install rake-compiler 85 | # rake-compiler cross-ruby VERSION=1.9.3-p327 86 | # 87 | ### install cross-compiled ruby 1.8.7 88 | # rbenv install 1.8.7-p371 89 | # gem install rake-compiler 90 | # rake-compiler cross-ruby VERSION=1.8.7-p371 91 | # 92 | ### build gem 93 | # rbenv shell 1.8.7-p371 94 | # gem install bundler && bundle 95 | # rake cross native gem RUBY_CC_VERSION=1.8.7:1.9.3:2.0.0 96 | # 97 | 98 | -------------------------------------------------------------------------------- /cbor.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | require 'cbor/version' 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "cbor" 6 | s.version = CBOR::VERSION 7 | s.summary = "CBOR, Concise Binary Object Representation." 8 | s.description = %q{CBOR is a library for the CBOR binary object representation format, based on Sadayuki Furuhashi's MessagePack library.} 9 | s.author = "Carsten Bormann, standing on the tall shoulders of Sadayuki Furuhashi" 10 | s.email = "cabo@tzi.org" 11 | s.license = "Apache-2.0" 12 | s.homepage = "http://cbor.io/" 13 | # s.has_rdoc = false 14 | s.files = `git ls-files`.split("\n") 15 | s.test_files = `git ls-files -- {test,spec}/*`.split("\n") 16 | s.require_paths = ["lib"] 17 | s.extensions = ["ext/cbor/extconf.rb"] 18 | 19 | s.add_development_dependency 'bundler' 20 | s.add_development_dependency 'rake', ['~> 0.9.2'] 21 | s.add_development_dependency 'rake-compiler', ['~> 0.8.3'] 22 | s.add_development_dependency 'rspec', ['~> 2.11'] 23 | s.add_development_dependency 'json', ['~> 1.7'] 24 | s.add_development_dependency 'yard', ['~> 0.9.11'] 25 | end 26 | -------------------------------------------------------------------------------- /doclib/cbor.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | # 3 | # Serializes an object into an IO or String. 4 | # 5 | # @overload encode(obj) 6 | # @return [String] serialized data 7 | # 8 | # @overload encode(obj, io) 9 | # @return [IO] 10 | # 11 | def self.encode(arg) 12 | end 13 | 14 | # 15 | # Serializes an object into an IO or String. Alias of encode. 16 | # 17 | # @overload dump(obj) 18 | # @return [String] serialized data 19 | # 20 | # @overload dump(obj, io) 21 | # @return [IO] 22 | # 23 | def self.dump(arg) 24 | end 25 | 26 | # 27 | # Serializes an object into an IO or String. Alias of encode. 28 | # 29 | # @overload dump(obj) 30 | # @return [String] serialized data 31 | # 32 | # @overload dump(obj, io) 33 | # @return [IO] 34 | # 35 | def self.pack(arg) 36 | end 37 | 38 | # 39 | # Deserializes an object from an IO or String. 40 | # 41 | # @overload decode(string) 42 | # @param string [String] data to deserialize 43 | # 44 | # @overload decode(io) 45 | # @param io [IO] 46 | # 47 | # @return [Object] deserialized object 48 | # 49 | def self.decode(arg) 50 | end 51 | 52 | # 53 | # Deserializes an object from an IO or String. Alias of decode. 54 | # 55 | # @overload load(string) 56 | # @param string [String] data to deserialize 57 | # 58 | # @overload load(io) 59 | # @param io [IO] 60 | # 61 | # @return [Object] deserialized object 62 | # 63 | def self.load(arg) 64 | end 65 | 66 | # 67 | # Deserializes an object from an IO or String. Alias of decode. 68 | # 69 | # @overload unpack(string) 70 | # @param string [String] data to deserialize 71 | # 72 | # @overload unpack(io) 73 | # @param io [IO] 74 | # 75 | # @return [Object] deserialized object 76 | # 77 | def self.unpack(arg) 78 | end 79 | end 80 | 81 | -------------------------------------------------------------------------------- /doclib/cbor/buffer.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | 3 | class Buffer 4 | # 5 | # Creates a CBOR::Buffer instance. 6 | # 7 | # @overload initialize(options={}) 8 | # @param options [Hash] 9 | # 10 | # @overload initialize(io, options={}) 11 | # @param io [IO] 12 | # @param options [Hash] 13 | # This buffer writes written data into the IO when it is filled. 14 | # This buffer reads data from the IO when it is empty. 15 | # 16 | # _io_ must respond to readpartial(length, [,string]) or read(string) method and 17 | # write(string) or append(string) method. 18 | # 19 | # Supported options: 20 | # 21 | # * *:io_buffer_size* buffer size to read data from the internal IO. (default: 32768) 22 | # * *:read_reference_threshold* the threshold size to enable zero-copy deserialize optimization. Read strings longer than this threshold will refer the original string instead of copying it. (default: 256) (supported in MRI only) 23 | # * *:write_reference_threshold* the threshold size to enable zero-copy serialize optimization. The buffer refers written strings longer than this threshold instead of copying it. (default: 524288) (supported in MRI only) 24 | # 25 | def initialize(*args) 26 | end 27 | 28 | # 29 | # Makes the buffer empty 30 | # 31 | # @return nil 32 | # 33 | def clear 34 | end 35 | 36 | # 37 | # Returns byte size of the buffer. 38 | # 39 | # @return nil 40 | # 41 | def size 42 | end 43 | 44 | # 45 | # Returns _true_ if the buffer is empty. 46 | # This method is slightly faster than _size_. 47 | # 48 | # @return [Boolean] 49 | # 50 | def empty? 51 | end 52 | 53 | # 54 | # Appends the given data to the buffer. 55 | # 56 | # @param data [String] 57 | # @return [Integer] byte size written 58 | # 59 | def write(data) 60 | end 61 | 62 | # 63 | # Appends the given data to the buffer. 64 | # 65 | # @param data [String] 66 | # @return [Buffer] self 67 | # 68 | def <<(data) 69 | end 70 | 71 | # 72 | # Consumes _n_ bytes from the head of the buffer and returns consumed data. 73 | # If the size of the buffer is less than _n_, it reads all of data in the buffer. 74 | # 75 | # If _n_ is 0, it does nothing and returns an empty string. 76 | # If the optional _buffer_ argument is given, the content of the string will be replaced with the consumed data. 77 | # 78 | # @overload read 79 | # 80 | # @overload read(n) 81 | # @param n [Integer] bytes to read 82 | # 83 | # @overload read(n, buffer) 84 | # @param n [Integer] bytes to read 85 | # @param buffer [String] buffer to read into 86 | # 87 | # @return [String] 88 | # 89 | def read(n) 90 | end 91 | 92 | # 93 | # Consumes _n_ bytes from the head of the buffer and returns consumed data. 94 | # If the size of the buffer is less than _n_, it does nothing and raises EOFError. 95 | # 96 | # If _n_ is 0, it does nothing and returns an empty string. 97 | # If the optional _buffer_ argument is given, the content of the string will be replaced with the consumed data. 98 | # 99 | # @overload read_all 100 | # 101 | # @overload read_all(n) 102 | # @param n [Integer] bytes to read 103 | # 104 | # @overload read_all(n, buffer) 105 | # @param n [Integer] bytes to read 106 | # @param buffer [String] buffer to read into 107 | # 108 | # @return [String] 109 | # 110 | def read_all(n, buffer=nil) 111 | end 112 | 113 | # 114 | # Consumes _n_ bytes from the head of the buffer. 115 | # If the size of the buffer is less than _n_, it skips all of data in the buffer and returns integer less than _n_. 116 | # 117 | # If _n_ is 0, it does nothing and returns _0_. 118 | # 119 | # @param n [Integer] byte size to skip 120 | # @return [Integer] byte size actually skipped 121 | # 122 | def skip(n) 123 | end 124 | 125 | # 126 | # Consumes _n_ bytes from the head of the buffer. 127 | # If the size of the buffer is less than _n_, it does nothing and raises EOFError. 128 | # If _n_ is 0, it does nothing. 129 | # 130 | # @param n [Integer] byte size to skip 131 | # @return [Buffer] self 132 | # 133 | def skip_all(n) 134 | end 135 | 136 | # 137 | # Returns all data in the buffer as a string. 138 | # Destructive update to the returned string does NOT effect the buffer. 139 | # 140 | # @return [String] 141 | # 142 | def to_str 143 | end 144 | 145 | # 146 | # Returns content of the buffer as an array of strings. 147 | # 148 | # This method is sometimes faster than to_s because the internal 149 | # structure of the buffer is a queue of buffer chunks. 150 | # 151 | # @return [Array] array of strings 152 | # 153 | def to_a 154 | end 155 | 156 | # 157 | # Internal io 158 | # 159 | # @return IO 160 | # 161 | attr_reader :io 162 | 163 | # 164 | # Flushes data in the internal buffer to the internal IO. 165 | # If internal IO is not set, it does nothing. 166 | # 167 | # @return [Buffer] self 168 | # 169 | def flush 170 | end 171 | 172 | # 173 | # Closes internal IO if its set. 174 | # If internal IO is not set, it does nothing 175 | # 176 | # @return nil 177 | # 178 | def close 179 | end 180 | 181 | # 182 | # Writes all of data in the internal buffer into the given IO. 183 | # This method consumes and removes data from the internal buffer. 184 | # _io_ must respond to write(data) method. 185 | # 186 | # @param io [IO] 187 | # @return [Integer] byte size of written data 188 | # 189 | def write_to(io) 190 | end 191 | end 192 | 193 | end 194 | -------------------------------------------------------------------------------- /doclib/cbor/core_ext.rb: -------------------------------------------------------------------------------- 1 | 2 | class NilClass 3 | # 4 | # Same as CBOR.encode(self[, io]). 5 | # 6 | # @return [String] serialized data 7 | # 8 | def to_cbor(io=nil) 9 | end 10 | end 11 | 12 | class TrueClass 13 | # 14 | # Same as CBOR.encode(self[, io]). 15 | # 16 | # @return [String] serialized data 17 | # 18 | def to_cbor(io=nil) 19 | end 20 | end 21 | 22 | class FalseClass 23 | # 24 | # Same as CBOR.encode(self[, io]). 25 | # 26 | # @return [String] serialized data 27 | # 28 | def to_cbor(io=nil) 29 | end 30 | end 31 | 32 | class Fixnum < Integer 33 | # 34 | # Same as CBOR.encode(self[, io]). 35 | # 36 | # @return [String] serialized data 37 | # 38 | def to_cbor(io=nil) 39 | end 40 | end 41 | 42 | class Bignum < Integer 43 | # 44 | # Same as CBOR.encode(self[, io]). 45 | # 46 | # @return [String] serialized data 47 | # 48 | def to_cbor(io=nil) 49 | end 50 | end 51 | 52 | class Float < Numeric 53 | # 54 | # Same as CBOR.encode(self[, io]). 55 | # 56 | # @return [String] serialized data 57 | # 58 | def to_cbor(io=nil) 59 | end 60 | end 61 | 62 | class String 63 | # 64 | # Same as CBOR.encode(self[, io]). 65 | # 66 | # @return [String] serialized data 67 | # 68 | def to_cbor(io=nil) 69 | end 70 | end 71 | 72 | class Array 73 | # 74 | # Same as CBOR.encode(self[, io]). 75 | # 76 | # @return [String] serialized data 77 | # 78 | def to_cbor(io=nil) 79 | end 80 | end 81 | 82 | class Hash 83 | # 84 | # Same as CBOR.encode(self[, io]). 85 | # 86 | # @return [String] serialized data 87 | # 88 | def to_cbor(io=nil) 89 | end 90 | end 91 | 92 | class Time 93 | # 94 | # Same as CBOR.encode(self[, io]). 95 | # 96 | # @return [String] serialized data 97 | # 98 | def to_cbor(io=nil) 99 | end 100 | end 101 | 102 | 103 | class Regexp 104 | # 105 | # Same as CBOR.encode(self[, io]). 106 | # 107 | # @return [String] serialized data 108 | # 109 | def to_cbor(io=nil) 110 | end 111 | end 112 | 113 | class URI 114 | # 115 | # Same as CBOR.encode(self[, io]). This method is only defined if 116 | # the class URI was defined when the CBOR extension was loaded, so 117 | # +require "uri"+ before +require "cbor"+ if you need this. 118 | # 119 | # @return [String] serialized data 120 | # 121 | def to_cbor(io=nil) 122 | end 123 | end 124 | 125 | class Symbol 126 | # 127 | # Same as CBOR.encode(self[, io]). 128 | # 129 | # @return [String] serialized data 130 | # 131 | def to_cbor(io=nil) 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /doclib/cbor/error.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | 3 | class UnpackError < StandardError 4 | end 5 | 6 | class MalformedFormatError < UnpackError 7 | end 8 | 9 | class StackError < UnpackError 10 | end 11 | 12 | class TypeError < StandardError 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /doclib/cbor/packer.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | 3 | # 4 | # CBOR::Packer is an interface to serialize objects into an internal buffer, 5 | # which is a CBOR::Buffer. 6 | # 7 | class Packer 8 | # 9 | # Creates a CBOR::Packer instance. 10 | # See Buffer#initialize for supported options. 11 | # 12 | # @overload initialize(options={}) 13 | # @param options [Hash] 14 | # 15 | # @overload initialize(io, options={}) 16 | # @param io [IO] 17 | # @param options [Hash] 18 | # This packer writes serialzied objects into the IO when the internal buffer is filled. 19 | # _io_ must respond to write(string) or append(string) method. 20 | # 21 | def initialize(*args) 22 | end 23 | 24 | # 25 | # Internal buffer 26 | # 27 | # @return CBOR::Buffer 28 | # 29 | attr_reader :buffer 30 | 31 | # 32 | # Serializes an object into internal buffer. 33 | # 34 | # If it could not serialize the object, it raises 35 | # NoMethodError: undefined method `to_cbor' for #. 36 | # 37 | # @param obj [Object] object to serialize 38 | # @return [Packer] self 39 | # 40 | def write(obj) 41 | end 42 | 43 | alias pack write 44 | 45 | # 46 | # Serializes a nil object. Same as write(nil). 47 | # 48 | def write_nil 49 | end 50 | 51 | # 52 | # Write a header of an array whose size is _n_. 53 | # For example, write_array_header(1).write(true) is same as write([ true ]). 54 | # 55 | # @return [Packer] self 56 | # 57 | def write_array_header(n) 58 | end 59 | 60 | # 61 | # Write a header of an map whose size is _n_. 62 | # For example, write_map_header(1).write('key').write(true) is same as write('key'=>true). 63 | # 64 | # @return [Packer] self 65 | # 66 | def write_map_header(n) 67 | end 68 | 69 | # 70 | # Flushes data in the internal buffer to the internal IO. Same as _buffer.flush. 71 | # If internal IO is not set, it does nothing. 72 | # 73 | # @return [Packer] self 74 | # 75 | def flush 76 | end 77 | 78 | # 79 | # Makes the internal buffer empty. Same as _buffer.clear_. 80 | # 81 | # @return nil 82 | # 83 | def clear 84 | end 85 | 86 | # 87 | # Returns size of the internal buffer. Same as buffer.size. 88 | # 89 | # @return [Integer] 90 | # 91 | def size 92 | end 93 | 94 | # 95 | # Returns _true_ if the internal buffer is empty. Same as buffer.empty?. 96 | # This method is slightly faster than _size_. 97 | # 98 | # @return [Boolean] 99 | # 100 | def empty? 101 | end 102 | 103 | # 104 | # Returns all data in the buffer as a string. Same as buffer.to_str. 105 | # 106 | # @return [String] 107 | # 108 | def to_str 109 | end 110 | 111 | alias to_s to_str 112 | 113 | # 114 | # Returns content of the internal buffer as an array of strings. Same as buffer.to_a. 115 | # This method is faster than _to_str_. 116 | # 117 | # @return [Array] array of strings 118 | # 119 | def to_a 120 | end 121 | 122 | # 123 | # Writes all of data in the internal buffer into the given IO. Same as buffer.write_to(io). 124 | # This method consumes and removes data from the internal buffer. 125 | # _io_ must respond to write(data) method. 126 | # 127 | # @param io [IO] 128 | # @return [Integer] byte size of written data 129 | # 130 | def write_to(io) 131 | end 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /doclib/cbor/simple.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | 3 | # 4 | # CBOR::Simple is used to carry around simple values that don't map 5 | # into Ruby classes (false, true, nil are the ones that do). 6 | class Simple 7 | 8 | # @param value [Integer] The integer number of the simple value 9 | def initialize(value) 10 | end 11 | 12 | attr_reader :value 13 | 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /doclib/cbor/tagged.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | 3 | # 4 | # CBOR::Tagged is used to carry around tagged values that don't map 5 | # into Ruby classes (false, true, nil are the ones that do). 6 | class Tagged 7 | 8 | # @param tag [Integer] The integer number of the tag 9 | # @param value [Object] The value that is being tagged 10 | def initialize(tag, value) 11 | end 12 | 13 | attr_reader :tag, :value 14 | 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /doclib/cbor/unpacker.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | 3 | # 4 | # CBOR::Unpacker is an interface to deserialize objects from an internal buffer, 5 | # which is a CBOR::Buffer. 6 | # 7 | class Unpacker 8 | # 9 | # Creates a CBOR::Unpacker instance. 10 | # 11 | # @overload initialize(options={}) 12 | # @param options [Hash] 13 | # 14 | # @overload initialize(io, options={}) 15 | # @param io [IO] 16 | # @param options [Hash] 17 | # This unpacker reads data from the _io_ to fill the internal buffer. 18 | # _io_ must respond to readpartial(length [,string]) or read(length [,string]) method. 19 | # 20 | # See Buffer#initialize for supported options. 21 | # 22 | def initialize(*args) 23 | end 24 | 25 | # 26 | # Internal buffer 27 | # 28 | # @return [CBOR::Buffer] 29 | # 30 | attr_reader :buffer 31 | 32 | # 33 | # Deserializes an object from internal buffer and returns it. 34 | # 35 | # If there're not enough buffer, this method raises EOFError. 36 | # If data format is invalid, this method raises CBOR::MalformedFormatError. 37 | # If the stack is too deep, this method raises CBOR::StackError. 38 | # 39 | # @return [Object] deserialized object 40 | # 41 | def read 42 | end 43 | 44 | alias unpack read 45 | 46 | # 47 | # Deserializes an object and ignores it. This method is faster than _read_. 48 | # 49 | # This method could raise same errors with _read_. 50 | # 51 | # @return nil 52 | # 53 | def skip 54 | end 55 | 56 | # 57 | # Deserializes a nil value if it exists and returns _true_. 58 | # Otherwise, if a byte exists but the byte doesn't represent nil value, 59 | # returns _false_. 60 | # 61 | # If there're not enough buffer, this method raises EOFError. 62 | # 63 | # @return [Boolean] 64 | # 65 | def skip_nil 66 | end 67 | 68 | # 69 | # Read a header of an array and returns its size. 70 | # It converts a serialized array into a stream of elements. 71 | # 72 | # If the serialized object is not an array, it raises CBOR::TypeError. 73 | # If there're not enough buffer, this method raises EOFError. 74 | # 75 | # @return [Integer] size of the array 76 | # 77 | def read_array_header 78 | end 79 | 80 | # 81 | # Reads a header of an map and returns its size. 82 | # It converts a serialized map into a stream of key-value pairs. 83 | # 84 | # If the serialized object is not a map, it raises CBOR::TypeError. 85 | # If there're not enough buffer, this method raises EOFError. 86 | # 87 | # @return [Integer] size of the map 88 | # 89 | def read_map_header 90 | end 91 | 92 | # 93 | # Appends data into the internal buffer. 94 | # This method calls buffer.append(data). 95 | # 96 | # @param data [String] 97 | # @return [Unpacker] self 98 | # 99 | def feed(data) 100 | end 101 | 102 | # 103 | # Repeats to deserialize objects. 104 | # 105 | # It repeats until the internal buffer does not include any complete objects. 106 | # 107 | # If the an IO is set, it repeats to read data from the IO when the buffer 108 | # becomes empty until the IO raises EOFError. 109 | # 110 | # This method could raise same errors with _read_ excepting EOFError. 111 | # 112 | # @yieldparam object [Object] deserialized object 113 | # @return nil 114 | # 115 | def each(&block) 116 | end 117 | 118 | # 119 | # Appends data into the internal buffer and repeats to deserialize objects. 120 | # This method is equals to feed(data) && each. 121 | # 122 | # @param data [String] 123 | # @yieldparam object [Object] deserialized object 124 | # @return nil 125 | # 126 | def feed_each(data, &block) 127 | end 128 | 129 | # 130 | # Resets deserialization state of the unpacker and clears the internal buffer. 131 | # 132 | # @return nil 133 | # 134 | def reset 135 | end 136 | end 137 | 138 | end 139 | -------------------------------------------------------------------------------- /ext/cbor/buffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "buffer.h" 29 | #include "rmem.h" 30 | 31 | #ifdef COMPAT_HAVE_ENCODING /* see compat.h*/ 32 | int s_enc_ascii8bit; 33 | int s_enc_usascii; 34 | int s_enc_utf8; 35 | VALUE s_enc_utf8_value; 36 | #endif 37 | 38 | #ifndef HAVE_RB_STR_REPLACE 39 | static ID s_replace; 40 | #endif 41 | 42 | #ifndef DISABLE_RMEM 43 | static msgpack_rmem_t s_rmem; 44 | #endif 45 | 46 | void msgpack_buffer_static_init() 47 | { 48 | #ifndef DISABLE_RMEM 49 | msgpack_rmem_init(&s_rmem); 50 | #endif 51 | #ifndef HAVE_RB_STR_REPLACE 52 | s_replace = rb_intern("replace"); 53 | #endif 54 | 55 | #ifdef COMPAT_HAVE_ENCODING 56 | s_enc_ascii8bit = rb_ascii8bit_encindex(); 57 | s_enc_usascii = rb_usascii_encindex(); 58 | s_enc_utf8 = rb_utf8_encindex(); 59 | s_enc_utf8_value = rb_enc_from_encoding(rb_utf8_encoding()); 60 | #endif 61 | } 62 | 63 | void msgpack_buffer_static_destroy() 64 | { 65 | #ifndef DISABLE_RMEM 66 | msgpack_rmem_destroy(&s_rmem); 67 | #endif 68 | } 69 | 70 | void msgpack_buffer_init(msgpack_buffer_t* b) 71 | { 72 | memset(b, 0, sizeof(msgpack_buffer_t)); 73 | 74 | b->head = &b->tail; 75 | b->write_reference_threshold = MSGPACK_BUFFER_STRING_WRITE_REFERENCE_DEFAULT; 76 | b->read_reference_threshold = MSGPACK_BUFFER_STRING_READ_REFERENCE_DEFAULT; 77 | b->io_buffer_size = MSGPACK_BUFFER_IO_BUFFER_SIZE_DEFAULT; 78 | b->io = Qnil; 79 | b->io_buffer = Qnil; 80 | } 81 | 82 | static void _msgpack_buffer_chunk_destroy(msgpack_buffer_chunk_t* c) 83 | { 84 | if(c->mem != NULL) { 85 | #ifndef DISABLE_RMEM 86 | if(!msgpack_rmem_free(&s_rmem, c->mem)) { 87 | free(c->mem); 88 | } 89 | /* no needs to update rmem_owner because chunks will not be 90 | * free()ed (left in free_list) and thus *rmem_owner is 91 | * always valid. */ 92 | #else 93 | free(c->mem); 94 | #endif 95 | } 96 | c->first = NULL; 97 | c->last = NULL; 98 | c->mem = NULL; 99 | } 100 | 101 | void msgpack_buffer_destroy(msgpack_buffer_t* b) 102 | { 103 | /* head is always available */ 104 | msgpack_buffer_chunk_t* c = b->head; 105 | while(c != &b->tail) { 106 | msgpack_buffer_chunk_t* n = c->next; 107 | _msgpack_buffer_chunk_destroy(c); 108 | free(c); 109 | c = n; 110 | } 111 | _msgpack_buffer_chunk_destroy(c); 112 | 113 | c = b->free_list; 114 | while(c != NULL) { 115 | msgpack_buffer_chunk_t* n = c->next; 116 | free(c); 117 | c = n; 118 | } 119 | } 120 | 121 | void msgpack_buffer_mark(msgpack_buffer_t* b) 122 | { 123 | /* head is always available */ 124 | msgpack_buffer_chunk_t* c = b->head; 125 | while(c != &b->tail) { 126 | rb_gc_mark(c->mapped_string); 127 | c = c->next; 128 | } 129 | rb_gc_mark(c->mapped_string); 130 | 131 | rb_gc_mark(b->io); 132 | rb_gc_mark(b->io_buffer); 133 | 134 | rb_gc_mark(b->owner); 135 | } 136 | 137 | bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b) 138 | { 139 | _msgpack_buffer_chunk_destroy(b->head); 140 | 141 | if(b->head == &b->tail) { 142 | /* list becomes empty. don't add head to free_list 143 | * because head should be always available */ 144 | b->tail_buffer_end = NULL; 145 | b->read_buffer = NULL; 146 | return false; 147 | } 148 | 149 | /* add head to free_list */ 150 | msgpack_buffer_chunk_t* next_head = b->head->next; 151 | b->head->next = b->free_list; 152 | b->free_list = b->head; 153 | 154 | b->head = next_head; 155 | b->read_buffer = next_head->first; 156 | 157 | return true; 158 | } 159 | 160 | void msgpack_buffer_clear(msgpack_buffer_t* b) 161 | { 162 | while(_msgpack_buffer_shift_chunk(b)) { 163 | ; 164 | } 165 | } 166 | 167 | size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string, size_t length) 168 | { 169 | size_t avail = msgpack_buffer_top_readable_size(b); 170 | 171 | #ifndef DISABLE_BUFFER_READ_REFERENCE_OPTIMIZE 172 | /* optimize */ 173 | if(length <= avail && RSTRING_LEN(string) == 0 && 174 | b->head->mapped_string != NO_MAPPED_STRING && 175 | length >= b->read_reference_threshold) { 176 | VALUE s = _msgpack_buffer_refer_head_mapped_string(b, length); 177 | #ifndef HAVE_RB_STR_REPLACE 178 | /* TODO MRI 1.8 */ 179 | rb_funcall(string, s_replace, 1, s); 180 | #else 181 | rb_str_replace(string, s); 182 | #endif 183 | /* here doesn't have to call ENCODING_SET because 184 | * encoding of s is always ASCII-8BIT */ 185 | _msgpack_buffer_consumed(b, length); 186 | return length; 187 | } 188 | #endif 189 | 190 | size_t const length_orig = length; 191 | 192 | while(true) { 193 | if(length <= avail) { 194 | rb_str_buf_cat(string, b->read_buffer, length); 195 | _msgpack_buffer_consumed(b, length); 196 | return length_orig; 197 | } 198 | 199 | rb_str_buf_cat(string, b->read_buffer, avail); 200 | length -= avail; 201 | 202 | if(!_msgpack_buffer_shift_chunk(b)) { 203 | return length_orig - length; 204 | } 205 | 206 | avail = msgpack_buffer_top_readable_size(b); 207 | } 208 | } 209 | 210 | size_t msgpack_buffer_read_nonblock(msgpack_buffer_t* b, char* buffer, size_t length) 211 | { 212 | /* buffer == NULL means skip */ 213 | size_t const length_orig = length; 214 | 215 | while(true) { 216 | size_t avail = msgpack_buffer_top_readable_size(b); 217 | 218 | if(length <= avail) { 219 | if(buffer != NULL) { 220 | memcpy(buffer, b->read_buffer, length); 221 | } 222 | _msgpack_buffer_consumed(b, length); 223 | return length_orig; 224 | } 225 | 226 | if(buffer != NULL) { 227 | memcpy(buffer, b->read_buffer, avail); 228 | buffer += avail; 229 | } 230 | length -= avail; 231 | 232 | if(!_msgpack_buffer_shift_chunk(b)) { 233 | return length_orig - length; 234 | } 235 | } 236 | } 237 | 238 | size_t msgpack_buffer_all_readable_size(const msgpack_buffer_t* b) 239 | { 240 | size_t sz = msgpack_buffer_top_readable_size(b); 241 | 242 | if(b->head == &b->tail) { 243 | return sz; 244 | } 245 | 246 | msgpack_buffer_chunk_t* c = b->head->next; 247 | 248 | while(true) { 249 | sz += c->last - c->first; 250 | if(c == &b->tail) { 251 | return sz; 252 | } 253 | c = c->next; 254 | } 255 | } 256 | 257 | bool _msgpack_buffer_read_all2(msgpack_buffer_t* b, char* buffer, size_t length) 258 | { 259 | if(!msgpack_buffer_ensure_readable(b, length)) { 260 | return false; 261 | } 262 | 263 | msgpack_buffer_read_nonblock(b, buffer, length); 264 | return true; 265 | } 266 | 267 | 268 | static inline msgpack_buffer_chunk_t* _msgpack_buffer_alloc_new_chunk(msgpack_buffer_t* b) 269 | { 270 | msgpack_buffer_chunk_t* reuse = b->free_list; 271 | if(reuse == NULL) { 272 | return malloc(sizeof(msgpack_buffer_chunk_t)); 273 | } 274 | b->free_list = b->free_list->next; 275 | return reuse; 276 | } 277 | 278 | static inline void _msgpack_buffer_add_new_chunk(msgpack_buffer_t* b) 279 | { 280 | if(b->head == &b->tail) { 281 | if(b->tail.first == NULL) { 282 | /* empty buffer */ 283 | return; 284 | } 285 | 286 | msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b); 287 | 288 | *nc = b->tail; 289 | b->head = nc; 290 | nc->next = &b->tail; 291 | 292 | } else { 293 | /* search node before tail */ 294 | msgpack_buffer_chunk_t* before_tail = b->head; 295 | while(before_tail->next != &b->tail) { 296 | before_tail = before_tail->next; 297 | } 298 | 299 | msgpack_buffer_chunk_t* nc = _msgpack_buffer_alloc_new_chunk(b); 300 | 301 | #ifndef DISABLE_RMEM 302 | #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT 303 | if(b->rmem_last == b->tail_buffer_end) { 304 | /* reuse unused rmem space */ 305 | size_t unused = b->tail_buffer_end - b->tail.last; 306 | b->rmem_last -= unused; 307 | } 308 | #endif 309 | #endif 310 | 311 | /* rebuild tail */ 312 | *nc = b->tail; 313 | before_tail->next = nc; 314 | nc->next = &b->tail; 315 | } 316 | } 317 | 318 | static inline void _msgpack_buffer_append_reference(msgpack_buffer_t* b, VALUE string) 319 | { 320 | VALUE mapped_string = rb_str_dup(string); 321 | #ifdef COMPAT_HAVE_ENCODING 322 | ENCODING_SET(mapped_string, s_enc_ascii8bit); 323 | #endif 324 | 325 | _msgpack_buffer_add_new_chunk(b); 326 | 327 | char* data = RSTRING_PTR(mapped_string); 328 | size_t length = RSTRING_LEN(mapped_string); 329 | 330 | b->tail.first = (char*) data; 331 | b->tail.last = (char*) data + length; 332 | b->tail.mapped_string = mapped_string; 333 | b->tail.mem = NULL; 334 | 335 | /* msgpack_buffer_writable_size should return 0 for mapped chunk */ 336 | b->tail_buffer_end = b->tail.last; 337 | 338 | /* consider read_buffer */ 339 | if(b->head == &b->tail) { 340 | b->read_buffer = b->tail.first; 341 | } 342 | } 343 | 344 | void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string) 345 | { 346 | size_t length = RSTRING_LEN(string); 347 | 348 | if(b->io != Qnil) { 349 | msgpack_buffer_flush(b); 350 | rb_funcall(b->io, b->io_write_all_method, 1, string); 351 | 352 | } else if(!STR_DUP_LIKELY_DOES_COPY(string)) { 353 | _msgpack_buffer_append_reference(b, string); 354 | 355 | } else { 356 | msgpack_buffer_append(b, RSTRING_PTR(string), length); 357 | } 358 | } 359 | 360 | static inline void* _msgpack_buffer_chunk_malloc( 361 | msgpack_buffer_t* b, msgpack_buffer_chunk_t* c, 362 | size_t required_size, size_t* allocated_size) 363 | { 364 | #ifndef DISABLE_RMEM 365 | if(required_size <= MSGPACK_RMEM_PAGE_SIZE) { 366 | #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT 367 | if((size_t)(b->rmem_end - b->rmem_last) < required_size) { 368 | #endif 369 | /* alloc new rmem page */ 370 | *allocated_size = MSGPACK_RMEM_PAGE_SIZE; 371 | char* buffer = msgpack_rmem_alloc(&s_rmem); 372 | c->mem = buffer; 373 | 374 | /* update rmem owner */ 375 | b->rmem_owner = &c->mem; 376 | b->rmem_last = b->rmem_end = buffer + MSGPACK_RMEM_PAGE_SIZE; 377 | 378 | return buffer; 379 | 380 | #ifndef DISABLE_RMEM_REUSE_INTERNAL_FRAGMENT 381 | } else { 382 | /* reuse unused rmem */ 383 | *allocated_size = (size_t)(b->rmem_end - b->rmem_last); 384 | char* buffer = b->rmem_last; 385 | b->rmem_last = b->rmem_end; 386 | 387 | /* update rmem owner */ 388 | c->mem = *b->rmem_owner; 389 | *b->rmem_owner = NULL; 390 | b->rmem_owner = &c->mem; 391 | 392 | return buffer; 393 | } 394 | #endif 395 | } 396 | #else 397 | if(required_size < 72) { 398 | required_size = 72; 399 | } 400 | #endif 401 | 402 | // TODO alignment? 403 | *allocated_size = required_size; 404 | void* mem = malloc(required_size); 405 | c->mem = mem; 406 | return mem; 407 | } 408 | 409 | static inline void* _msgpack_buffer_chunk_realloc( 410 | msgpack_buffer_t* b, msgpack_buffer_chunk_t* c, 411 | void* mem, size_t required_size, size_t* current_size) 412 | { 413 | if(mem == NULL) { 414 | return _msgpack_buffer_chunk_malloc(b, c, required_size, current_size); 415 | } 416 | 417 | size_t next_size = *current_size * 2; 418 | while(next_size < required_size) { 419 | next_size *= 2; 420 | } 421 | *current_size = next_size; 422 | mem = realloc(mem, next_size); 423 | 424 | c->mem = mem; 425 | return mem; 426 | } 427 | 428 | void _msgpack_buffer_expand(msgpack_buffer_t* b, const char* data, size_t length, bool flush_to_io) 429 | { 430 | if(flush_to_io && b->io != Qnil) { 431 | msgpack_buffer_flush(b); 432 | if(msgpack_buffer_writable_size(b) >= length) { 433 | /* data == NULL means ensure_writable */ 434 | if(data != NULL) { 435 | size_t tail_avail = msgpack_buffer_writable_size(b); 436 | memcpy(b->tail.last, data, length); 437 | b->tail.last += tail_avail; 438 | } 439 | return; 440 | } 441 | } 442 | 443 | /* data == NULL means ensure_writable */ 444 | if(data != NULL) { 445 | size_t tail_avail = msgpack_buffer_writable_size(b); 446 | memcpy(b->tail.last, data, tail_avail); 447 | b->tail.last += tail_avail; 448 | data += tail_avail; 449 | length -= tail_avail; 450 | } 451 | 452 | size_t capacity = b->tail.last - b->tail.first; 453 | 454 | /* can't realloc mapped chunk or rmem page */ 455 | if(b->tail.mapped_string != NO_MAPPED_STRING 456 | #ifndef DISABLE_RMEM 457 | || capacity <= MSGPACK_RMEM_PAGE_SIZE 458 | #endif 459 | ) { 460 | /* allocate new chunk */ 461 | _msgpack_buffer_add_new_chunk(b); 462 | 463 | char* mem = _msgpack_buffer_chunk_malloc(b, &b->tail, length, &capacity); 464 | 465 | char* last = mem; 466 | if(data != NULL) { 467 | memcpy(mem, data, length); 468 | last += length; 469 | } 470 | 471 | /* rebuild tail chunk */ 472 | b->tail.first = mem; 473 | b->tail.last = last; 474 | b->tail.mapped_string = NO_MAPPED_STRING; 475 | b->tail_buffer_end = mem + capacity; 476 | 477 | /* consider read_buffer */ 478 | if(b->head == &b->tail) { 479 | b->read_buffer = b->tail.first; 480 | } 481 | 482 | } else { 483 | /* realloc malloc()ed chunk or NULL */ 484 | size_t tail_filled = b->tail.last - b->tail.first; 485 | char* mem = _msgpack_buffer_chunk_realloc(b, &b->tail, 486 | b->tail.first, tail_filled+length, &capacity); 487 | 488 | char* last = mem + tail_filled; 489 | if(data != NULL) { 490 | memcpy(last, data, length); 491 | last += length; 492 | } 493 | 494 | /* consider read_buffer */ 495 | if(b->head == &b->tail) { 496 | size_t read_offset = b->read_buffer - b->head->first; 497 | b->read_buffer = mem + read_offset; 498 | } 499 | 500 | /* rebuild tail chunk */ 501 | b->tail.first = mem; 502 | b->tail.last = last; 503 | b->tail_buffer_end = mem + capacity; 504 | } 505 | } 506 | 507 | static inline VALUE _msgpack_buffer_head_chunk_as_string(msgpack_buffer_t* b) 508 | { 509 | size_t length = b->head->last - b->read_buffer; 510 | if(length == 0) { 511 | return rb_str_buf_new(0); 512 | } 513 | 514 | if(b->head->mapped_string != NO_MAPPED_STRING) { 515 | return _msgpack_buffer_refer_head_mapped_string(b, length); 516 | } 517 | 518 | return rb_str_new(b->read_buffer, length); 519 | } 520 | 521 | static inline VALUE _msgpack_buffer_chunk_as_string(msgpack_buffer_chunk_t* c) 522 | { 523 | size_t chunk_size = c->last - c->first; 524 | if(chunk_size == 0) { 525 | return rb_str_buf_new(0); 526 | } 527 | 528 | if(c->mapped_string != NO_MAPPED_STRING) { 529 | return rb_str_dup(c->mapped_string); 530 | } 531 | 532 | return rb_str_new(c->first, chunk_size); 533 | } 534 | 535 | VALUE msgpack_buffer_all_as_string(msgpack_buffer_t* b) 536 | { 537 | if(b->head == &b->tail) { 538 | return _msgpack_buffer_head_chunk_as_string(b); 539 | } 540 | 541 | size_t length = msgpack_buffer_all_readable_size(b); 542 | VALUE string = rb_str_new(NULL, length); 543 | char* buffer = RSTRING_PTR(string); 544 | 545 | size_t avail = msgpack_buffer_top_readable_size(b); 546 | memcpy(buffer, b->read_buffer, avail); 547 | buffer += avail; 548 | length -= avail; 549 | 550 | msgpack_buffer_chunk_t* c = b->head->next; 551 | 552 | while(true) { 553 | avail = c->last - c->first; 554 | memcpy(buffer, c->first, avail); 555 | 556 | if(length <= avail) { 557 | return string; 558 | } 559 | buffer += avail; 560 | length -= avail; 561 | 562 | c = c->next; 563 | } 564 | } 565 | 566 | VALUE msgpack_buffer_all_as_string_array(msgpack_buffer_t* b) 567 | { 568 | if(b->head == &b->tail) { 569 | VALUE s = msgpack_buffer_all_as_string(b); 570 | VALUE ary = rb_ary_new3(1, s); 571 | return ary; 572 | } 573 | 574 | /* TODO optimize ary construction */ 575 | VALUE ary = rb_ary_new(); 576 | 577 | VALUE s = _msgpack_buffer_head_chunk_as_string(b); 578 | rb_ary_push(ary, s); 579 | 580 | msgpack_buffer_chunk_t* c = b->head->next; 581 | 582 | while(true) { 583 | s = _msgpack_buffer_chunk_as_string(c); 584 | rb_ary_push(ary, s); 585 | if(c == &b->tail) { 586 | return ary; 587 | } 588 | c = c->next; 589 | } 590 | 591 | return ary; 592 | } 593 | 594 | size_t msgpack_buffer_flush_to_io(msgpack_buffer_t* b, VALUE io, ID write_method, bool consume) 595 | { 596 | if(msgpack_buffer_top_readable_size(b) == 0) { 597 | return 0; 598 | } 599 | 600 | VALUE s = _msgpack_buffer_head_chunk_as_string(b); 601 | rb_funcall(io, write_method, 1, s); 602 | size_t sz = RSTRING_LEN(s); 603 | 604 | if(consume) { 605 | while(_msgpack_buffer_shift_chunk(b)) { 606 | s = _msgpack_buffer_chunk_as_string(b->head); 607 | rb_funcall(io, write_method, 1, s); 608 | sz += RSTRING_LEN(s); 609 | } 610 | return sz; 611 | 612 | } else { 613 | if(b->head == &b->tail) { 614 | return sz; 615 | } 616 | msgpack_buffer_chunk_t* c = b->head->next; 617 | while(true) { 618 | s = _msgpack_buffer_chunk_as_string(c); 619 | rb_funcall(io, write_method, 1, s); 620 | sz += RSTRING_LEN(s); 621 | if(c == &b->tail) { 622 | return sz; 623 | } 624 | c = c->next; 625 | } 626 | } 627 | } 628 | 629 | size_t _msgpack_buffer_feed_from_io(msgpack_buffer_t* b) 630 | { 631 | if(b->io_buffer == Qnil) { 632 | b->io_buffer = rb_funcall(b->io, b->io_partial_read_method, 1, LONG2NUM(b->io_buffer_size)); 633 | if(b->io_buffer == Qnil) { 634 | rb_raise(rb_eEOFError, "IO reached end of file"); 635 | } 636 | StringValue(b->io_buffer); 637 | } else { 638 | VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2NUM(b->io_buffer_size), b->io_buffer); 639 | if(ret == Qnil) { 640 | rb_raise(rb_eEOFError, "IO reached end of file"); 641 | } 642 | } 643 | 644 | size_t len = RSTRING_LEN(b->io_buffer); 645 | if(len == 0) { 646 | rb_raise(rb_eEOFError, "IO reached end of file"); 647 | } 648 | 649 | /* TODO zero-copy optimize? */ 650 | msgpack_buffer_append_nonblock(b, RSTRING_PTR(b->io_buffer), len); 651 | 652 | return len; 653 | } 654 | 655 | size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string, size_t length) 656 | { 657 | if(RSTRING_LEN(string) == 0) { 658 | /* direct read */ 659 | VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2NUM(length), string); 660 | if(ret == Qnil) { 661 | return 0; 662 | } 663 | return RSTRING_LEN(string); 664 | } 665 | 666 | /* copy via io_buffer */ 667 | if(b->io_buffer == Qnil) { 668 | b->io_buffer = rb_str_buf_new(0); 669 | } 670 | 671 | VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2NUM(length), b->io_buffer); 672 | if(ret == Qnil) { 673 | return 0; 674 | } 675 | size_t rl = RSTRING_LEN(b->io_buffer); 676 | 677 | rb_str_buf_cat(string, (const void*)RSTRING_PTR(b->io_buffer), rl); 678 | return rl; 679 | } 680 | 681 | size_t _msgpack_buffer_skip_from_io(msgpack_buffer_t* b, size_t length) 682 | { 683 | if(b->io_buffer == Qnil) { 684 | b->io_buffer = rb_str_buf_new(0); 685 | } 686 | 687 | VALUE ret = rb_funcall(b->io, b->io_partial_read_method, 2, LONG2NUM(length), b->io_buffer); 688 | if(ret == Qnil) { 689 | return 0; 690 | } 691 | return RSTRING_LEN(b->io_buffer); 692 | } 693 | 694 | -------------------------------------------------------------------------------- /ext/cbor/buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_BUFFER_H__ 28 | #define MSGPACK_RUBY_BUFFER_H__ 29 | 30 | #include "cbor.h" 31 | 32 | #include "compat.h" 33 | #include "sysdep.h" 34 | 35 | #ifdef COMPAT_HAVE_ENCODING /* see compat.h*/ 36 | extern int s_enc_ascii8bit; 37 | extern int s_enc_usascii; 38 | extern int s_enc_utf8; 39 | extern VALUE s_enc_utf8_value; 40 | #endif 41 | 42 | extern VALUE rb_cCBOR_Tagged; 43 | extern VALUE rb_cCBOR_Simple; 44 | 45 | #ifndef MSGPACK_BUFFER_STRING_WRITE_REFERENCE_DEFAULT 46 | #define MSGPACK_BUFFER_STRING_WRITE_REFERENCE_DEFAULT (512*1024) 47 | #endif 48 | 49 | /* at least 23 (RSTRING_EMBED_LEN_MAX) bytes */ 50 | #ifndef MSGPACK_BUFFER_STRING_WRITE_REFERENCE_MINIMUM 51 | #define MSGPACK_BUFFER_STRING_WRITE_REFERENCE_MINIMUM 256 52 | #endif 53 | 54 | #ifndef MSGPACK_BUFFER_STRING_READ_REFERENCE_DEFAULT 55 | #define MSGPACK_BUFFER_STRING_READ_REFERENCE_DEFAULT 256 56 | #endif 57 | 58 | /* at least 23 (RSTRING_EMBED_LEN_MAX) bytes */ 59 | #ifndef MSGPACK_BUFFER_STRING_READ_REFERENCE_MINIMUM 60 | #define MSGPACK_BUFFER_STRING_READ_REFERENCE_MINIMUM 256 61 | #endif 62 | 63 | #ifndef MSGPACK_BUFFER_IO_BUFFER_SIZE_DEFAULT 64 | #define MSGPACK_BUFFER_IO_BUFFER_SIZE_DEFAULT (32*1024) 65 | #endif 66 | 67 | #ifndef MSGPACK_BUFFER_IO_BUFFER_SIZE_MINIMUM 68 | #define MSGPACK_BUFFER_IO_BUFFER_SIZE_MINIMUM (1024) 69 | #endif 70 | 71 | #define NO_MAPPED_STRING ((VALUE)0) 72 | 73 | struct msgpack_buffer_chunk_t; 74 | typedef struct msgpack_buffer_chunk_t msgpack_buffer_chunk_t; 75 | 76 | struct msgpack_buffer_t; 77 | typedef struct msgpack_buffer_t msgpack_buffer_t; 78 | 79 | /* 80 | * msgpack_buffer_chunk_t 81 | * +----------------+ 82 | * | filled | free | 83 | * +---------+------+ 84 | * ^ first ^ last 85 | */ 86 | struct msgpack_buffer_chunk_t { 87 | char* first; 88 | char* last; 89 | void* mem; 90 | msgpack_buffer_chunk_t* next; 91 | VALUE mapped_string; /* RBString or NO_MAPPED_STRING */ 92 | }; 93 | 94 | union msgpack_buffer_cast_block_t { 95 | char buffer[8]; 96 | uint8_t u8; 97 | uint16_t u16; 98 | uint32_t u32; 99 | uint64_t u64; 100 | int8_t i8; 101 | int16_t i16; 102 | int32_t i32; 103 | int64_t i64; 104 | float f; 105 | double d; 106 | }; 107 | 108 | struct msgpack_buffer_t { 109 | char* read_buffer; 110 | char* tail_buffer_end; 111 | 112 | msgpack_buffer_chunk_t tail; 113 | msgpack_buffer_chunk_t* head; 114 | msgpack_buffer_chunk_t* free_list; 115 | 116 | #ifndef DISABLE_RMEM 117 | char* rmem_last; 118 | char* rmem_end; 119 | void** rmem_owner; 120 | #endif 121 | 122 | union msgpack_buffer_cast_block_t cast_block; 123 | 124 | VALUE io; 125 | VALUE io_buffer; 126 | ID io_write_all_method; 127 | ID io_partial_read_method; 128 | 129 | size_t write_reference_threshold; 130 | size_t read_reference_threshold; 131 | size_t io_buffer_size; 132 | 133 | VALUE owner; 134 | }; 135 | 136 | /* 137 | * initialization functions 138 | */ 139 | void msgpack_buffer_static_init(); 140 | 141 | void msgpack_buffer_static_destroy(); 142 | 143 | void msgpack_buffer_init(msgpack_buffer_t* b); 144 | 145 | void msgpack_buffer_destroy(msgpack_buffer_t* b); 146 | 147 | void msgpack_buffer_mark(msgpack_buffer_t* b); 148 | 149 | void msgpack_buffer_clear(msgpack_buffer_t* b); 150 | 151 | static inline void msgpack_buffer_set_write_reference_threshold(msgpack_buffer_t* b, size_t length) 152 | { 153 | if(length < MSGPACK_BUFFER_STRING_WRITE_REFERENCE_MINIMUM) { 154 | length = MSGPACK_BUFFER_STRING_WRITE_REFERENCE_MINIMUM; 155 | } 156 | b->write_reference_threshold = length; 157 | } 158 | 159 | static inline void msgpack_buffer_set_read_reference_threshold(msgpack_buffer_t* b, size_t length) 160 | { 161 | if(length < MSGPACK_BUFFER_STRING_READ_REFERENCE_MINIMUM) { 162 | length = MSGPACK_BUFFER_STRING_READ_REFERENCE_MINIMUM; 163 | } 164 | b->read_reference_threshold = length; 165 | } 166 | 167 | static inline void msgpack_buffer_set_io_buffer_size(msgpack_buffer_t* b, size_t length) 168 | { 169 | if(length < MSGPACK_BUFFER_IO_BUFFER_SIZE_MINIMUM) { 170 | length = MSGPACK_BUFFER_IO_BUFFER_SIZE_MINIMUM; 171 | } 172 | b->io_buffer_size = length; 173 | } 174 | 175 | static inline void msgpack_buffer_reset_io(msgpack_buffer_t* b) 176 | { 177 | b->io = Qnil; 178 | } 179 | 180 | static inline bool msgpack_buffer_has_io(msgpack_buffer_t* b) 181 | { 182 | return b->io != Qnil; 183 | } 184 | 185 | static inline void msgpack_buffer_reset(msgpack_buffer_t* b) 186 | { 187 | msgpack_buffer_clear(b); 188 | msgpack_buffer_reset_io(b); 189 | } 190 | 191 | 192 | /* 193 | * writer functions 194 | */ 195 | 196 | static inline size_t msgpack_buffer_writable_size(const msgpack_buffer_t* b) 197 | { 198 | return b->tail_buffer_end - b->tail.last; 199 | } 200 | 201 | static inline void msgpack_buffer_write_1(msgpack_buffer_t* b, int byte) 202 | { 203 | (*b->tail.last++) = (char) byte; 204 | } 205 | 206 | static inline void msgpack_buffer_write_2(msgpack_buffer_t* b, int byte1, unsigned char byte2) 207 | { 208 | *(b->tail.last++) = (char) byte1; 209 | *(b->tail.last++) = (char) byte2; 210 | } 211 | 212 | static inline void msgpack_buffer_write_byte_and_data(msgpack_buffer_t* b, int byte, const void* data, size_t length) 213 | { 214 | (*b->tail.last++) = (char) byte; 215 | 216 | memcpy(b->tail.last, data, length); 217 | b->tail.last += length; 218 | } 219 | 220 | void _msgpack_buffer_expand(msgpack_buffer_t* b, const char* data, size_t length, bool use_flush); 221 | 222 | size_t msgpack_buffer_flush_to_io(msgpack_buffer_t* b, VALUE io, ID write_method, bool consume); 223 | 224 | static inline size_t msgpack_buffer_flush(msgpack_buffer_t* b) 225 | { 226 | if(b->io == Qnil) { 227 | return 0; 228 | } 229 | return msgpack_buffer_flush_to_io(b, b->io, b->io_write_all_method, true); 230 | } 231 | 232 | static inline void msgpack_buffer_ensure_writable(msgpack_buffer_t* b, size_t require) 233 | { 234 | if(msgpack_buffer_writable_size(b) < require) { 235 | _msgpack_buffer_expand(b, NULL, require, true); 236 | } 237 | } 238 | 239 | static inline void _msgpack_buffer_append_impl(msgpack_buffer_t* b, const char* data, size_t length, bool flush_to_io) 240 | { 241 | if(length == 0) { 242 | return; 243 | } 244 | 245 | if(length <= msgpack_buffer_writable_size(b)) { 246 | memcpy(b->tail.last, data, length); 247 | b->tail.last += length; 248 | return; 249 | } 250 | 251 | _msgpack_buffer_expand(b, data, length, flush_to_io); 252 | } 253 | 254 | static inline void msgpack_buffer_append(msgpack_buffer_t* b, const char* data, size_t length) 255 | { 256 | _msgpack_buffer_append_impl(b, data, length, true); 257 | } 258 | 259 | static inline void msgpack_buffer_append_nonblock(msgpack_buffer_t* b, const char* data, size_t length) 260 | { 261 | _msgpack_buffer_append_impl(b, data, length, false); 262 | } 263 | 264 | void _msgpack_buffer_append_long_string(msgpack_buffer_t* b, VALUE string); 265 | 266 | static inline size_t msgpack_buffer_append_string(msgpack_buffer_t* b, VALUE string) 267 | { 268 | size_t length = RSTRING_LEN(string); 269 | 270 | if(length > b->write_reference_threshold) { 271 | _msgpack_buffer_append_long_string(b, string); 272 | 273 | } else { 274 | msgpack_buffer_append(b, RSTRING_PTR(string), length); 275 | } 276 | 277 | return length; 278 | } 279 | 280 | 281 | /* 282 | * IO functions 283 | */ 284 | size_t _msgpack_buffer_feed_from_io(msgpack_buffer_t* b); 285 | 286 | size_t _msgpack_buffer_read_from_io_to_string(msgpack_buffer_t* b, VALUE string, size_t length); 287 | 288 | size_t _msgpack_buffer_skip_from_io(msgpack_buffer_t* b, size_t length); 289 | 290 | 291 | /* 292 | * reader functions 293 | */ 294 | 295 | static inline size_t msgpack_buffer_top_readable_size(const msgpack_buffer_t* b) 296 | { 297 | return b->head->last - b->read_buffer; 298 | } 299 | 300 | size_t msgpack_buffer_all_readable_size(const msgpack_buffer_t* b); 301 | 302 | bool _msgpack_buffer_shift_chunk(msgpack_buffer_t* b); 303 | 304 | static inline void _msgpack_buffer_consumed(msgpack_buffer_t* b, size_t length) 305 | { 306 | b->read_buffer += length; 307 | if(b->read_buffer >= b->head->last) { 308 | _msgpack_buffer_shift_chunk(b); 309 | } 310 | } 311 | 312 | static inline int msgpack_buffer_peek_top_1(msgpack_buffer_t* b) 313 | { 314 | return (int) (unsigned char) b->read_buffer[0]; 315 | } 316 | 317 | static inline int msgpack_buffer_read_top_1(msgpack_buffer_t* b) 318 | { 319 | int r = (int) (unsigned char) b->read_buffer[0]; 320 | 321 | _msgpack_buffer_consumed(b, 1); 322 | 323 | return r; 324 | } 325 | 326 | static inline int msgpack_buffer_read_1(msgpack_buffer_t* b) 327 | { 328 | if(msgpack_buffer_top_readable_size(b) <= 0) { 329 | if(b->io == Qnil) { 330 | return -1; 331 | } 332 | _msgpack_buffer_feed_from_io(b); 333 | } 334 | 335 | int r = (int) (unsigned char) b->read_buffer[0]; 336 | _msgpack_buffer_consumed(b, 1); 337 | 338 | return r; 339 | } 340 | 341 | 342 | /* 343 | * bulk read / skip functions 344 | */ 345 | 346 | size_t msgpack_buffer_read_nonblock(msgpack_buffer_t* b, char* buffer, size_t length); 347 | 348 | static inline bool msgpack_buffer_ensure_readable(msgpack_buffer_t* b, size_t require) 349 | { 350 | if(msgpack_buffer_top_readable_size(b) < require) { 351 | size_t sz = msgpack_buffer_all_readable_size(b); 352 | if(sz < require) { 353 | if(b->io == Qnil) { 354 | return false; 355 | } 356 | do { 357 | size_t rl = _msgpack_buffer_feed_from_io(b); 358 | sz += rl; 359 | } while(sz < require); 360 | } 361 | } 362 | return true; 363 | } 364 | 365 | bool _msgpack_buffer_read_all2(msgpack_buffer_t* b, char* buffer, size_t length); 366 | 367 | static inline bool msgpack_buffer_read_all(msgpack_buffer_t* b, char* buffer, size_t length) 368 | { 369 | size_t avail = msgpack_buffer_top_readable_size(b); 370 | if(avail < length) { 371 | return _msgpack_buffer_read_all2(b, buffer, length); 372 | } 373 | 374 | memcpy(buffer, b->read_buffer, length); 375 | _msgpack_buffer_consumed(b, length); 376 | return true; 377 | } 378 | 379 | static inline size_t msgpack_buffer_skip_nonblock(msgpack_buffer_t* b, size_t length) 380 | { 381 | size_t avail = msgpack_buffer_top_readable_size(b); 382 | if(avail < length) { 383 | return msgpack_buffer_read_nonblock(b, NULL, length); 384 | } 385 | _msgpack_buffer_consumed(b, length); 386 | return length; 387 | } 388 | 389 | static inline union msgpack_buffer_cast_block_t* msgpack_buffer_read_cast_block(msgpack_buffer_t* b, size_t n) 390 | { 391 | if(!msgpack_buffer_read_all(b, b->cast_block.buffer, n)) { 392 | return NULL; 393 | } 394 | return &b->cast_block; 395 | } 396 | 397 | size_t msgpack_buffer_read_to_string_nonblock(msgpack_buffer_t* b, VALUE string, size_t length); 398 | 399 | static inline size_t msgpack_buffer_read_to_string(msgpack_buffer_t* b, VALUE string, size_t length) 400 | { 401 | if(length == 0) { 402 | return 0; 403 | } 404 | 405 | size_t avail = msgpack_buffer_top_readable_size(b); 406 | if(avail > 0) { 407 | return msgpack_buffer_read_to_string_nonblock(b, string, length); 408 | } else if(b->io != Qnil) { 409 | return _msgpack_buffer_read_from_io_to_string(b, string, length); 410 | } else { 411 | return 0; 412 | } 413 | } 414 | 415 | static inline size_t msgpack_buffer_skip(msgpack_buffer_t* b, size_t length) 416 | { 417 | if(length == 0) { 418 | return 0; 419 | } 420 | 421 | size_t avail = msgpack_buffer_top_readable_size(b); 422 | if(avail > 0) { 423 | return msgpack_buffer_skip_nonblock(b, length); 424 | } else if(b->io != Qnil) { 425 | return _msgpack_buffer_skip_from_io(b, length); 426 | } else { 427 | return 0; 428 | } 429 | } 430 | 431 | 432 | VALUE msgpack_buffer_all_as_string(msgpack_buffer_t* b); 433 | 434 | VALUE msgpack_buffer_all_as_string_array(msgpack_buffer_t* b); 435 | 436 | static inline VALUE _msgpack_buffer_refer_head_mapped_string(msgpack_buffer_t* b, size_t length) 437 | { 438 | size_t offset = b->read_buffer - b->head->first; 439 | return rb_str_substr(b->head->mapped_string, offset, length); 440 | } 441 | 442 | static inline VALUE msgpack_buffer_read_top_as_string(msgpack_buffer_t* b, size_t length, bool will_be_frozen, bool as_symbol) 443 | { 444 | #ifndef DISABLE_BUFFER_READ_REFERENCE_OPTIMIZE 445 | /* optimize */ 446 | if(!will_be_frozen && 447 | b->head->mapped_string != NO_MAPPED_STRING && 448 | length >= b->read_reference_threshold) { 449 | VALUE result = _msgpack_buffer_refer_head_mapped_string(b, length); 450 | _msgpack_buffer_consumed(b, length); 451 | return result; 452 | } 453 | #endif 454 | 455 | VALUE result; 456 | if (as_symbol) { 457 | #ifndef HAVE_RB_STR_INTERN 458 | #ifndef HAVE_RB_INTERN_STR 459 | /* MRI 1.8 doesn't have rb_intern_str or rb_intern2, hack it... */ 460 | char *tmp = xmalloc(length+1); 461 | memcpy(tmp, b->read_buffer, length); 462 | tmp[length] = 0; 463 | result = ID2SYM(rb_intern(tmp)); 464 | xfree(tmp); 465 | #else 466 | result = ID2SYM(rb_intern2(b->read_buffer, length)); 467 | /* FIXME: This is stuck at ASCII encoding */ 468 | #endif 469 | #else 470 | /* enable GC-able symbols here: */ 471 | result = rb_str_intern(rb_str_new(b->read_buffer, length)); 472 | #endif 473 | /* todo: rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc) */ 474 | } else { 475 | result = rb_str_new(b->read_buffer, length); 476 | /* todo: use rb_enc_str_new(const char *ptr, long len, rb_encoding *enc) :: */ 477 | } 478 | _msgpack_buffer_consumed(b, length); 479 | return result; 480 | } 481 | 482 | 483 | #endif 484 | 485 | -------------------------------------------------------------------------------- /ext/cbor/buffer_class.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "compat.h" 29 | #include "ruby.h" 30 | #include "buffer.h" 31 | #include "buffer_class.h" 32 | 33 | VALUE cMessagePack_Buffer; 34 | 35 | static ID s_read; 36 | static ID s_readpartial; 37 | static ID s_write; 38 | static ID s_append; 39 | static ID s_close; 40 | 41 | #define BUFFER(from, name) \ 42 | msgpack_buffer_t *name = NULL; \ 43 | Data_Get_Struct(from, msgpack_buffer_t, name); \ 44 | if(name == NULL) { \ 45 | rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \ 46 | } 47 | 48 | #define CHECK_STRING_TYPE(value) \ 49 | value = rb_check_string_type(value); \ 50 | if( NIL_P(value) ) { \ 51 | rb_raise(rb_eTypeError, "instance of String needed"); \ 52 | } 53 | 54 | static void Buffer_free(void* data) 55 | { 56 | if(data == NULL) { 57 | return; 58 | } 59 | msgpack_buffer_t* b = (msgpack_buffer_t*) data; 60 | msgpack_buffer_destroy(b); 61 | xfree(b); 62 | } 63 | 64 | static VALUE Buffer_alloc(VALUE klass) 65 | { 66 | msgpack_buffer_t* b = ALLOC_N(msgpack_buffer_t, 1); 67 | msgpack_buffer_init(b); 68 | 69 | return Data_Wrap_Struct(klass, msgpack_buffer_mark, Buffer_free, b); 70 | } 71 | 72 | static ID get_partial_read_method(VALUE io) 73 | { 74 | if(rb_respond_to(io, s_readpartial)) { 75 | return s_readpartial; 76 | } else if(rb_respond_to(io, s_read)) { 77 | return s_read; 78 | } else { 79 | return s_read; 80 | } 81 | } 82 | 83 | static ID get_write_all_method(VALUE io) 84 | { 85 | if(rb_respond_to(io, s_write)) { 86 | return s_write; 87 | } else if(rb_respond_to(io, s_append)) { 88 | return s_append; 89 | } else { 90 | return s_write; 91 | } 92 | } 93 | 94 | void MessagePack_Buffer_initialize(msgpack_buffer_t* b, VALUE io, VALUE options) 95 | { 96 | b->io = io; 97 | b->io_partial_read_method = get_partial_read_method(io); 98 | b->io_write_all_method = get_write_all_method(io); 99 | 100 | if(options != Qnil) { 101 | VALUE v; 102 | 103 | v = rb_hash_aref(options, ID2SYM(rb_intern("read_reference_threshold"))); 104 | if(v != Qnil) { 105 | msgpack_buffer_set_read_reference_threshold(b, NUM2ULONG(v)); 106 | } 107 | 108 | v = rb_hash_aref(options, ID2SYM(rb_intern("write_reference_threshold"))); 109 | if(v != Qnil) { 110 | msgpack_buffer_set_write_reference_threshold(b, NUM2ULONG(v)); 111 | } 112 | 113 | v = rb_hash_aref(options, ID2SYM(rb_intern("io_buffer_size"))); 114 | if(v != Qnil) { 115 | msgpack_buffer_set_io_buffer_size(b, NUM2ULONG(v)); 116 | } 117 | } 118 | } 119 | 120 | VALUE MessagePack_Buffer_wrap(msgpack_buffer_t* b, VALUE owner) 121 | { 122 | b->owner = owner; 123 | return Data_Wrap_Struct(cMessagePack_Buffer, msgpack_buffer_mark, NULL, b); 124 | } 125 | 126 | static VALUE Buffer_initialize(int argc, VALUE* argv, VALUE self) 127 | { 128 | VALUE io = Qnil; 129 | VALUE options = Qnil; 130 | 131 | if(argc == 0 || (argc == 1 && argv[0] == Qnil)) { 132 | /* Qnil */ 133 | 134 | } else if(argc == 1) { 135 | VALUE v = argv[0]; 136 | if(rb_type(v) == T_HASH) { 137 | options = v; 138 | } else { 139 | io = v; 140 | } 141 | 142 | } else if(argc == 2) { 143 | io = argv[0]; 144 | options = argv[1]; 145 | if(rb_type(options) != T_HASH) { 146 | rb_raise(rb_eArgError, "expected Hash but found %s.", rb_obj_classname(io)); 147 | } 148 | 149 | } else { 150 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc); 151 | } 152 | 153 | BUFFER(self, b); 154 | 155 | MessagePack_Buffer_initialize(b, io, options); 156 | 157 | return self; 158 | } 159 | 160 | static VALUE Buffer_clear(VALUE self) 161 | { 162 | BUFFER(self, b); 163 | msgpack_buffer_clear(b); 164 | return Qnil; 165 | } 166 | 167 | static VALUE Buffer_size(VALUE self) 168 | { 169 | BUFFER(self, b); 170 | size_t size = msgpack_buffer_all_readable_size(b); 171 | return SIZET2NUM(size); 172 | } 173 | 174 | static VALUE Buffer_empty_p(VALUE self) 175 | { 176 | BUFFER(self, b); 177 | if(msgpack_buffer_top_readable_size(b) == 0) { 178 | return Qtrue; 179 | } else { 180 | return Qfalse; 181 | } 182 | } 183 | 184 | static VALUE Buffer_write(VALUE self, VALUE string_or_buffer) 185 | { 186 | BUFFER(self, b); 187 | 188 | VALUE string = string_or_buffer; // TODO optimize if string_or_buffer is a Buffer 189 | StringValue(string); 190 | 191 | size_t length = msgpack_buffer_append_string(b, string); 192 | 193 | return SIZET2NUM(length); 194 | } 195 | 196 | static VALUE Buffer_append(VALUE self, VALUE string_or_buffer) 197 | { 198 | BUFFER(self, b); 199 | 200 | VALUE string = string_or_buffer; // TODO optimize if string_or_buffer is a Buffer 201 | StringValue(string); 202 | 203 | msgpack_buffer_append_string(b, string); 204 | 205 | return self; 206 | } 207 | 208 | 209 | #define MAKE_EMPTY_STRING(orig) \ 210 | if(orig == Qnil) { \ 211 | orig = rb_str_buf_new(0); \ 212 | } else { \ 213 | rb_str_resize(orig, 0); \ 214 | } 215 | 216 | static VALUE read_until_eof_rescue(VALUE args) 217 | { 218 | msgpack_buffer_t* b = (void*) ((VALUE*) args)[0]; 219 | VALUE out = ((VALUE*) args)[1]; 220 | unsigned long max = ((VALUE*) args)[2]; 221 | size_t* sz = (void*) ((VALUE*) args)[3]; 222 | 223 | while(true) { 224 | size_t rl; 225 | if(max == 0) { 226 | if(out == Qnil) { 227 | rl = msgpack_buffer_skip(b, b->io_buffer_size); 228 | } else { 229 | rl = msgpack_buffer_read_to_string(b, out, b->io_buffer_size); 230 | } 231 | if(rl == 0) { 232 | break; 233 | } 234 | *sz += rl; 235 | 236 | } else { 237 | if(out == Qnil) { 238 | rl = msgpack_buffer_skip(b, max); 239 | } else { 240 | rl = msgpack_buffer_read_to_string(b, out, max); 241 | } 242 | if(rl == 0) { 243 | break; 244 | } 245 | *sz += rl; 246 | if(max <= rl) { 247 | break; 248 | } else { 249 | max -= rl; 250 | } 251 | } 252 | } 253 | 254 | return Qnil; 255 | } 256 | 257 | static VALUE read_until_eof_error(VALUE args, VALUE error) 258 | { 259 | /* ignore EOFError */ 260 | UNUSED(args); 261 | UNUSED(error); 262 | return Qnil; 263 | } 264 | 265 | static inline size_t read_until_eof(msgpack_buffer_t* b, VALUE out, unsigned long max) 266 | { 267 | if(msgpack_buffer_has_io(b)) { 268 | size_t sz = 0; 269 | VALUE args[4] = { (VALUE)(void*) b, out, (VALUE) max, (VALUE)(void*) &sz }; 270 | rb_rescue2(read_until_eof_rescue, (VALUE)(void*) args, 271 | read_until_eof_error, (VALUE)(void*) args, 272 | rb_eEOFError, NULL); 273 | return sz; 274 | 275 | } else { 276 | if(max == 0) { 277 | max = ULONG_MAX; 278 | } 279 | if(out == Qnil) { 280 | return msgpack_buffer_skip_nonblock(b, max); 281 | } else { 282 | return msgpack_buffer_read_to_string_nonblock(b, out, max); 283 | } 284 | } 285 | } 286 | 287 | static inline VALUE read_all(msgpack_buffer_t* b, VALUE out) 288 | { 289 | #ifndef DISABLE_BUFFER_READ_TO_S_OPTIMIZE 290 | if(out == Qnil && !msgpack_buffer_has_io(b)) { 291 | /* same as to_s && clear; optimize */ 292 | VALUE str = msgpack_buffer_all_as_string(b); 293 | msgpack_buffer_clear(b); 294 | return str; 295 | } 296 | #endif 297 | MAKE_EMPTY_STRING(out); 298 | read_until_eof(b, out, 0); 299 | return out; 300 | } 301 | 302 | static VALUE Buffer_skip(VALUE self, VALUE sn) 303 | { 304 | BUFFER(self, b); 305 | 306 | unsigned long n = FIX2ULONG(sn); 307 | 308 | /* do nothing */ 309 | if(n == 0) { 310 | return ULONG2NUM(0); 311 | } 312 | 313 | size_t sz = read_until_eof(b, Qnil, n); 314 | return ULONG2NUM(sz); 315 | } 316 | 317 | static VALUE Buffer_skip_all(VALUE self, VALUE sn) 318 | { 319 | BUFFER(self, b); 320 | 321 | unsigned long n = FIX2ULONG(sn); 322 | 323 | /* do nothing */ 324 | if(n == 0) { 325 | return self; 326 | } 327 | 328 | if(!msgpack_buffer_ensure_readable(b, n)) { 329 | rb_raise(rb_eEOFError, "end of buffer reached"); 330 | } 331 | 332 | msgpack_buffer_skip_nonblock(b, n); 333 | 334 | return self; 335 | } 336 | 337 | static VALUE Buffer_read_all(int argc, VALUE* argv, VALUE self) 338 | { 339 | VALUE out = Qnil; 340 | unsigned long n = 0; 341 | bool all = false; 342 | 343 | switch(argc) { 344 | case 2: 345 | out = argv[1]; 346 | /* pass through */ 347 | case 1: 348 | n = FIX2ULONG(argv[0]); 349 | break; 350 | case 0: 351 | all = true; 352 | break; 353 | default: 354 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..2)", argc); 355 | } 356 | 357 | BUFFER(self, b); 358 | 359 | if(out != Qnil) { 360 | CHECK_STRING_TYPE(out); 361 | } 362 | 363 | if(all) { 364 | return read_all(b, out); 365 | } 366 | 367 | if(n == 0) { 368 | /* do nothing */ 369 | MAKE_EMPTY_STRING(out); 370 | return out; 371 | } 372 | 373 | if(!msgpack_buffer_ensure_readable(b, n)) { 374 | rb_raise(rb_eEOFError, "end of buffer reached"); 375 | } 376 | 377 | MAKE_EMPTY_STRING(out); 378 | msgpack_buffer_read_to_string_nonblock(b, out, n); 379 | 380 | return out; 381 | } 382 | 383 | static VALUE Buffer_read(int argc, VALUE* argv, VALUE self) 384 | { 385 | VALUE out = Qnil; 386 | unsigned long n = -1; 387 | bool all = false; 388 | 389 | switch(argc) { 390 | case 2: 391 | out = argv[1]; 392 | /* pass through */ 393 | case 1: 394 | n = FIX2ULONG(argv[0]); 395 | break; 396 | case 0: 397 | all = true; 398 | break; 399 | default: 400 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..2)", argc); 401 | } 402 | 403 | BUFFER(self, b); 404 | 405 | if(out != Qnil) { 406 | CHECK_STRING_TYPE(out); 407 | } 408 | 409 | if(all) { 410 | return read_all(b, out); 411 | } 412 | 413 | if(n == 0) { 414 | /* do nothing */ 415 | MAKE_EMPTY_STRING(out); 416 | return out; 417 | } 418 | 419 | #ifndef DISABLE_BUFFER_READ_TO_S_OPTIMIZE 420 | if(!msgpack_buffer_has_io(b) && out == Qnil && 421 | msgpack_buffer_all_readable_size(b) <= n) { 422 | /* same as to_s && clear; optimize */ 423 | VALUE str = msgpack_buffer_all_as_string(b); 424 | msgpack_buffer_clear(b); 425 | 426 | if(RSTRING_LEN(str) == 0) { 427 | return Qnil; 428 | } else { 429 | return str; 430 | } 431 | } 432 | #endif 433 | 434 | MAKE_EMPTY_STRING(out); 435 | read_until_eof(b, out, n); 436 | 437 | if(RSTRING_LEN(out) == 0) { 438 | return Qnil; 439 | } else { 440 | return out; 441 | } 442 | } 443 | 444 | static VALUE Buffer_to_str(VALUE self) 445 | { 446 | BUFFER(self, b); 447 | return msgpack_buffer_all_as_string(b); 448 | } 449 | 450 | static VALUE Buffer_to_a(VALUE self) 451 | { 452 | BUFFER(self, b); 453 | return msgpack_buffer_all_as_string_array(b); 454 | } 455 | 456 | static VALUE Buffer_flush(VALUE self) 457 | { 458 | BUFFER(self, b); 459 | msgpack_buffer_flush(b); 460 | return self; 461 | } 462 | 463 | static VALUE Buffer_io(VALUE self) 464 | { 465 | BUFFER(self, b); 466 | return b->io; 467 | } 468 | 469 | static VALUE Buffer_close(VALUE self) 470 | { 471 | BUFFER(self, b); 472 | if(b->io != Qnil) { 473 | return rb_funcall(b->io, s_close, 0); 474 | } 475 | return Qnil; 476 | } 477 | 478 | static VALUE Buffer_write_to(VALUE self, VALUE io) 479 | { 480 | BUFFER(self, b); 481 | size_t sz = msgpack_buffer_flush_to_io(b, io, s_write, true); 482 | return ULONG2NUM(sz); 483 | } 484 | 485 | void MessagePack_Buffer_module_init(VALUE mMessagePack) 486 | { 487 | s_read = rb_intern("read"); 488 | s_readpartial = rb_intern("readpartial"); 489 | s_write = rb_intern("write"); 490 | s_append = rb_intern("<<"); 491 | s_close = rb_intern("close"); 492 | 493 | msgpack_buffer_static_init(); 494 | 495 | cMessagePack_Buffer = rb_define_class_under(mMessagePack, "Buffer", rb_cObject); 496 | 497 | rb_define_alloc_func(cMessagePack_Buffer, Buffer_alloc); 498 | 499 | rb_define_method(cMessagePack_Buffer, "initialize", Buffer_initialize, -1); 500 | rb_define_method(cMessagePack_Buffer, "clear", Buffer_clear, 0); 501 | rb_define_method(cMessagePack_Buffer, "size", Buffer_size, 0); 502 | rb_define_method(cMessagePack_Buffer, "empty?", Buffer_empty_p, 0); 503 | rb_define_method(cMessagePack_Buffer, "write", Buffer_write, 1); 504 | rb_define_method(cMessagePack_Buffer, "<<", Buffer_append, 1); 505 | rb_define_method(cMessagePack_Buffer, "skip", Buffer_skip, 1); 506 | rb_define_method(cMessagePack_Buffer, "skip_all", Buffer_skip_all, 1); 507 | rb_define_method(cMessagePack_Buffer, "read", Buffer_read, -1); 508 | rb_define_method(cMessagePack_Buffer, "read_all", Buffer_read_all, -1); 509 | rb_define_method(cMessagePack_Buffer, "io", Buffer_io, 0); 510 | rb_define_method(cMessagePack_Buffer, "flush", Buffer_flush, 0); 511 | rb_define_method(cMessagePack_Buffer, "close", Buffer_close, 0); 512 | rb_define_method(cMessagePack_Buffer, "write_to", Buffer_write_to, 1); 513 | rb_define_method(cMessagePack_Buffer, "to_str", Buffer_to_str, 0); 514 | rb_define_alias(cMessagePack_Buffer, "to_s", "to_str"); 515 | rb_define_method(cMessagePack_Buffer, "to_a", Buffer_to_a, 0); 516 | } 517 | 518 | -------------------------------------------------------------------------------- /ext/cbor/buffer_class.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_BUFFER_CLASS_H__ 28 | #define MSGPACK_RUBY_BUFFER_CLASS_H__ 29 | 30 | #include "buffer.h" 31 | 32 | extern VALUE cMessagePack_Buffer; 33 | 34 | void MessagePack_Buffer_module_init(VALUE mMessagePack); 35 | 36 | VALUE MessagePack_Buffer_wrap(msgpack_buffer_t* b, VALUE owner); 37 | 38 | void MessagePack_Buffer_initialize(msgpack_buffer_t* b, VALUE io, VALUE options); 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /ext/cbor/cbor.h: -------------------------------------------------------------------------------- 1 | #ifndef CBOR_RUBY_BUFFER_H__ 2 | #define CBOR_RUBY_BUFFER_H__ 3 | 4 | /* The 8 major types */ 5 | #define MT_UNSIGNED 0 6 | #define MT_NEGATIVE 1 7 | #define MT_BYTES 2 8 | #define MT_TEXT 3 9 | #define MT_ARRAY 4 10 | #define MT_MAP 5 11 | #define MT_TAG 6 12 | #define MT_PRIM 7 13 | 14 | /* The initial bytes resulting from those */ 15 | #define IB_UNSIGNED (MT_UNSIGNED << 5) 16 | #define IB_NEGATIVE (MT_NEGATIVE << 5) 17 | #define IB_BYTES (MT_BYTES << 5) 18 | #define IB_TEXT (MT_TEXT << 5) 19 | #define IB_ARRAY (MT_ARRAY << 5) 20 | #define IB_MAP (MT_MAP << 5) 21 | #define IB_TAG (MT_TAG << 5) 22 | #define IB_PRIM (MT_PRIM << 5) 23 | 24 | #define IB_NEGFLAG (IB_NEGATIVE - IB_UNSIGNED) 25 | #define IB_NEGFLAG_AS_BIT(ib) ((ib) >> 5) 26 | #define IB_TEXTFLAG (IB_TEXT - IB_BYTES) 27 | 28 | #define IB_AI(ib) ((ib) & 0x1F) 29 | #define IB_MT(ib) ((ib) >> 5) 30 | 31 | /* Tag numbers handled by this implementation */ 32 | #define TAG_TIME_EPOCH 1 33 | #define TAG_BIGNUM 2 34 | #define TAG_BIGNUM_NEG 3 35 | #define TAG_URI 32 36 | #define TAG_RE 35 37 | 38 | /* Initial bytes of those tag numbers */ 39 | #define IB_TIME_EPOCH (IB_TAG + TAG_TIME_EPOCH) 40 | #define IB_BIGNUM (IB_TAG + TAG_BIGNUM) 41 | #define IB_BIGNUM_NEG (IB_TAG + TAG_BIGNUM_NEG) 42 | /* TAG_URI and TAG_RE are non-immediate tags */ 43 | 44 | /* Simple values handled by this implementation */ 45 | #define VAL_NIL 22 46 | #define VAL_FALSE 20 47 | #define VAL_TRUE 21 48 | 49 | /* Initial bytes of those simple values */ 50 | #define IB_NIL (IB_PRIM + VAL_NIL) 51 | #define IB_FALSE (IB_PRIM + VAL_FALSE) 52 | #define IB_TRUE (IB_PRIM + VAL_TRUE) 53 | 54 | /* AI values with more data in head */ 55 | #define AI_1 24 56 | #define AI_2 25 57 | #define AI_4 26 58 | #define AI_8 27 59 | #define AI_INDEF 31 60 | #define IB_BREAK (IB_PRIM + AI_INDEF) 61 | /* For */ 62 | #define IB_UNUSED (IB_TAG + AI_INDEF) 63 | 64 | /* Floating point initial bytes */ 65 | #define IB_FLOAT2 (IB_PRIM + AI_2) 66 | #define IB_FLOAT4 (IB_PRIM + AI_4) 67 | #define IB_FLOAT8 (IB_PRIM + AI_8) 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /ext/cbor/compat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_COMPAT_H__ 28 | #define MSGPACK_RUBY_COMPAT_H__ 29 | 30 | #include "ruby.h" 31 | 32 | #if defined(HAVE_RUBY_ST_H) 33 | # include "ruby/st.h" /* ruby hash on Ruby 1.9 */ 34 | #elif defined(HAVE_ST_H) 35 | # include "st.h" /* ruby hash on Ruby 1.8 */ 36 | #endif 37 | 38 | 39 | /* 40 | * COMPAT_HAVE_ENCODING 41 | */ 42 | #ifdef HAVE_RUBY_ENCODING_H 43 | # include "ruby/encoding.h" 44 | # define COMPAT_HAVE_ENCODING 45 | #endif 46 | 47 | #if defined(__MACRUBY__) /* MacRuby */ 48 | # undef COMPAT_HAVE_ENCODING 49 | #endif 50 | 51 | 52 | /* 53 | * define STR_DUP_LIKELY_DOES_COPY 54 | * check rb_str_dup actually copies the string or not 55 | */ 56 | #if defined(RUBY_VM) && defined(FL_ALL) && defined(FL_USER1) && defined(FL_USER3) /* MRI 1.9 */ 57 | # define STR_DUP_LIKELY_DOES_COPY(str) FL_ALL(str, FL_USER1|FL_USER3) /* same as STR_ASSOC_P(str) */ 58 | 59 | #elif defined(FL_TEST) && defined(ELTS_SHARED) /* MRI 1.8 */ 60 | # define STR_DUP_LIKELY_DOES_COPY(str) (!FL_TEST(str, ELTS_SHARED)) 61 | 62 | //#elif defined(RUBINIUS) || defined(JRUBY) /* Rubinius and JRuby */ 63 | #else 64 | # define STR_DUP_LIKELY_DOES_COPY(str) (1) 65 | 66 | #endif 67 | 68 | 69 | /* 70 | * SIZET2NUM 71 | */ 72 | #ifndef SIZET2NUM /* MRI 1.8 */ 73 | # define SIZET2NUM(v) ULL2NUM(v) 74 | #endif 75 | 76 | 77 | /* 78 | * rb_errinfo() 79 | */ 80 | #if defined(RUBY_VM) /* MRI 1.9 */ 81 | # define COMPAT_RERAISE rb_exc_raise(rb_errinfo()) 82 | 83 | #elif defined(JRUBY) /* JRuby */ 84 | # define COMPAT_RERAISE rb_exc_raise(rb_gv_get("$!")) 85 | 86 | #else /* MRI 1.8 and Rubinius */ 87 | # define COMPAT_RERAISE rb_exc_raise(ruby_errinfo) 88 | #endif 89 | 90 | 91 | /* 92 | * RBIGNUM_POSITIVE_P 93 | */ 94 | #ifndef RBIGNUM_POSITIVE_P 95 | # if defined(RUBINIUS) /* Rubinius <= v1.2.3 */ 96 | # define RBIGNUM_POSITIVE_P(b) (rb_funcall(b, rb_intern(">="), 1, INT2FIX(0)) == Qtrue) 97 | 98 | # elif defined(JRUBY) /* JRuby */ 99 | # define RBIGNUM_POSITIVE_P(b) (rb_funcall(b, rb_intern(">="), 1, INT2FIX(0)) == Qtrue) 100 | # define rb_big2ull(b) rb_num2ull(b) 101 | /*#define rb_big2ll(b) rb_num2ll(b)*/ 102 | 103 | # else /* MRI 1.8 */ 104 | # define RBIGNUM_POSITIVE_P(b) (RBIGNUM(b)->sign) 105 | # endif 106 | #endif 107 | 108 | 109 | #ifndef HAVE_RB_INTEGER_UNPACK 110 | 111 | /* More MRI 1.8 */ 112 | #ifndef RBIGNUM_LEN 113 | #define RBIGNUM_LEN(b) (RBIGNUM(b)->len) 114 | #endif 115 | #ifndef RBIGNUM_DIGITS 116 | #ifndef RBIGNUM 117 | #define CANT_DO_BIGNUMS_FAST_ON_THIS_PLATFORM 118 | #endif 119 | #define RBIGNUM_DIGITS(b) (RBIGNUM(b)->digits) 120 | #endif 121 | #ifndef HAVE_RB_BIG_NEW 122 | /* not really worth fixing any more... */ 123 | #define CANT_DO_BIGNUMS_FAST_ON_THIS_PLATFORM 124 | /* gross 1.8.7 hack thanks to Mathieu Bouchard */ 125 | #define rb_big_new(len, sign) rb_funcall(INT2FIX(1),rb_intern("<<"),1,INT2FIX(len > 0 ? ((len) * SIZEOF_BDIGITS * 8) - 1 : 0)); 126 | #endif 127 | 128 | #endif 129 | 130 | #ifndef RB_TYPE_P 131 | #define RB_TYPE_P(obj, type) (TYPE(obj) == (type)) 132 | #endif 133 | 134 | /* 135 | * RSTRING_PTR, RSTRING_LEN 136 | */ 137 | #ifndef RSTRING_PTR /* MRI 1.8.5 */ 138 | # define RSTRING_PTR(s) (RSTRING(s)->ptr) 139 | #endif 140 | 141 | #ifndef RSTRING_LEN /* MRI 1.8.5 */ 142 | # define RSTRING_LEN(s) (RSTRING(s)->len) 143 | #endif 144 | 145 | 146 | #endif 147 | 148 | -------------------------------------------------------------------------------- /ext/cbor/core_ext.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "core_ext.h" 29 | #include "packer.h" 30 | #include "packer_class.h" 31 | 32 | static inline VALUE delegete_to_pack(int argc, VALUE* argv, VALUE self) 33 | { 34 | if(argc == 0) { 35 | return MessagePack_pack(1, &self); 36 | } else if(argc == 1) { 37 | /* write to io */ 38 | VALUE argv2[2]; 39 | argv2[0] = self; 40 | argv2[1] = argv[0]; 41 | return MessagePack_pack(2, argv2); 42 | } else { 43 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc); 44 | } 45 | } 46 | 47 | #define ENSURE_PACKER(argc, argv, packer, pk) \ 48 | if(argc != 1 || rb_class_of(argv[0]) != cMessagePack_Packer) { \ 49 | return delegete_to_pack(argc, argv, self); \ 50 | } \ 51 | VALUE packer = argv[0]; \ 52 | msgpack_packer_t *pk; \ 53 | Data_Get_Struct(packer, msgpack_packer_t, pk); 54 | 55 | static VALUE NilClass_to_msgpack(int argc, VALUE* argv, VALUE self) 56 | { 57 | ENSURE_PACKER(argc, argv, packer, pk); 58 | msgpack_packer_write_nil(pk); 59 | return packer; 60 | } 61 | 62 | static VALUE TrueClass_to_msgpack(int argc, VALUE* argv, VALUE self) 63 | { 64 | ENSURE_PACKER(argc, argv, packer, pk); 65 | msgpack_packer_write_true(pk); 66 | return packer; 67 | } 68 | 69 | static VALUE FalseClass_to_msgpack(int argc, VALUE* argv, VALUE self) 70 | { 71 | ENSURE_PACKER(argc, argv, packer, pk); 72 | msgpack_packer_write_false(pk); 73 | return packer; 74 | } 75 | 76 | #ifdef RUBY_INTEGER_UNIFICATION 77 | 78 | static VALUE Integer_to_msgpack(int argc, VALUE* argv, VALUE self) 79 | { 80 | ENSURE_PACKER(argc, argv, packer, pk); 81 | if (FIXNUM_P(self)) 82 | msgpack_packer_write_fixnum_value(pk, self); 83 | else 84 | msgpack_packer_write_bignum_value(pk, self); 85 | return packer; 86 | } 87 | 88 | #else 89 | 90 | static VALUE Fixnum_to_msgpack(int argc, VALUE* argv, VALUE self) 91 | { 92 | ENSURE_PACKER(argc, argv, packer, pk); 93 | msgpack_packer_write_fixnum_value(pk, self); 94 | return packer; 95 | } 96 | 97 | static VALUE Bignum_to_msgpack(int argc, VALUE* argv, VALUE self) 98 | { 99 | ENSURE_PACKER(argc, argv, packer, pk); 100 | msgpack_packer_write_bignum_value(pk, self); 101 | return packer; 102 | } 103 | 104 | #endif 105 | 106 | static VALUE Float_to_msgpack(int argc, VALUE* argv, VALUE self) 107 | { 108 | ENSURE_PACKER(argc, argv, packer, pk); 109 | msgpack_packer_write_float_value(pk, self); 110 | return packer; 111 | } 112 | 113 | static VALUE String_to_msgpack(int argc, VALUE* argv, VALUE self) 114 | { 115 | ENSURE_PACKER(argc, argv, packer, pk); 116 | msgpack_packer_write_string_value(pk, self); 117 | return packer; 118 | } 119 | 120 | static VALUE Array_to_msgpack(int argc, VALUE* argv, VALUE self) 121 | { 122 | ENSURE_PACKER(argc, argv, packer, pk); 123 | msgpack_packer_write_array_value(pk, self); 124 | return packer; 125 | } 126 | 127 | static VALUE Hash_to_msgpack(int argc, VALUE* argv, VALUE self) 128 | { 129 | ENSURE_PACKER(argc, argv, packer, pk); 130 | msgpack_packer_write_hash_value(pk, self); 131 | return packer; 132 | } 133 | 134 | static VALUE Symbol_to_msgpack(int argc, VALUE* argv, VALUE self) 135 | { 136 | ENSURE_PACKER(argc, argv, packer, pk); 137 | msgpack_packer_write_symbol_value(pk, self); 138 | return packer; 139 | } 140 | 141 | static VALUE Simple_to_msgpack(int argc, VALUE* argv, VALUE self) 142 | { 143 | ENSURE_PACKER(argc, argv, packer, pk); 144 | msgpack_packer_write_simple_value(pk, self); 145 | return packer; 146 | } 147 | 148 | static VALUE Tagged_to_msgpack(int argc, VALUE* argv, VALUE self) 149 | { 150 | ENSURE_PACKER(argc, argv, packer, pk); 151 | msgpack_packer_write_tagged_value(pk, self); 152 | return packer; 153 | } 154 | 155 | static VALUE Regexp_to_msgpack(int argc, VALUE* argv, VALUE self) 156 | { 157 | ENSURE_PACKER(argc, argv, packer, pk); 158 | msgpack_packer_write_processed_value(pk, self, rb_intern("source"), TAG_RE); 159 | return packer; 160 | } 161 | 162 | static VALUE URI_to_msgpack(int argc, VALUE* argv, VALUE self) 163 | { 164 | ENSURE_PACKER(argc, argv, packer, pk); 165 | msgpack_packer_write_processed_value(pk, self, rb_intern("to_s"), TAG_URI); 166 | return packer; 167 | } 168 | 169 | static VALUE Time_to_msgpack(int argc, VALUE* argv, VALUE self) 170 | { 171 | ENSURE_PACKER(argc, argv, packer, pk); 172 | msgpack_packer_write_processed_value(pk, self, rb_intern("to_i"), TAG_TIME_EPOCH); 173 | return packer; 174 | } 175 | 176 | void MessagePack_core_ext_module_init() 177 | { 178 | rb_define_method(rb_cNilClass, "to_cbor", NilClass_to_msgpack, -1); 179 | rb_define_method(rb_cTrueClass, "to_cbor", TrueClass_to_msgpack, -1); 180 | rb_define_method(rb_cFalseClass, "to_cbor", FalseClass_to_msgpack, -1); 181 | #ifdef RUBY_INTEGER_UNIFICATION 182 | rb_define_method(rb_cInteger, "to_cbor", Integer_to_msgpack, -1); 183 | #else 184 | rb_define_method(rb_cFixnum, "to_cbor", Fixnum_to_msgpack, -1); 185 | rb_define_method(rb_cBignum, "to_cbor", Bignum_to_msgpack, -1); 186 | #endif 187 | rb_define_method(rb_cFloat, "to_cbor", Float_to_msgpack, -1); 188 | rb_define_method(rb_cString, "to_cbor", String_to_msgpack, -1); 189 | rb_define_method(rb_cArray, "to_cbor", Array_to_msgpack, -1); 190 | rb_define_method(rb_cHash, "to_cbor", Hash_to_msgpack, -1); 191 | rb_define_method(rb_cSymbol, "to_cbor", Symbol_to_msgpack, -1); 192 | rb_define_method(rb_cTime, "to_cbor", Time_to_msgpack, -1); 193 | rb_define_method(rb_cRegexp, "to_cbor", Regexp_to_msgpack, -1); 194 | if (rb_const_defined(rb_cObject, rb_intern("URI"))) { 195 | rb_define_method(rb_const_get(rb_cObject, rb_intern("URI")), 196 | "to_cbor", URI_to_msgpack, -1); 197 | } 198 | rb_define_method(rb_cCBOR_Simple, "to_cbor", Simple_to_msgpack, -1); 199 | rb_define_method(rb_cCBOR_Tagged, "to_cbor", Tagged_to_msgpack, -1); 200 | } 201 | 202 | -------------------------------------------------------------------------------- /ext/cbor/core_ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_CORE_EXT_H__ 28 | #define MSGPACK_RUBY_CORE_EXT_H__ 29 | 30 | #include "compat.h" 31 | 32 | void MessagePack_core_ext_module_init(); 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /ext/cbor/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | have_header("ruby/st.h") 4 | have_header("st.h") 5 | have_func("rb_str_replace", ["ruby.h"]) 6 | have_func("rb_big_new", ["ruby.h"]) 7 | have_func("rb_intern_str", ["ruby.h"]) 8 | have_func("rb_sym2str", ["ruby.h"]) 9 | have_func("rb_str_intern", ["ruby.h"]) 10 | have_func("rb_integer_unpack", ["ruby.h"]) 11 | 12 | $CFLAGS << %[ -I.. -Wall -O3 -g -std=c99] 13 | #$CFLAGS << %[ -DDISABLE_RMEM] 14 | #$CFLAGS << %[ -DDISABLE_RMEM_REUSE_INTERNAL_FRAGMENT] 15 | #$CFLAGS << %[ -DDISABLE_BUFFER_READ_REFERENCE_OPTIMIZE] 16 | #$CFLAGS << %[ -DDISABLE_BUFFER_READ_TO_S_OPTIMIZE] 17 | 18 | if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx' 19 | # msgpack-ruby doesn't modify data came from RSTRING_PTR(str) 20 | $CFLAGS << %[ -DRSTRING_NOT_MODIFIED] 21 | # Rubinius C extensions don't grab GVL while rmem is not thread safe 22 | $CFLAGS << %[ -DDISABLE_RMEM] 23 | end 24 | 25 | if warnflags = CONFIG['warnflags'] 26 | warnflags.slice!(/ -Wdeclaration-after-statement/) 27 | end 28 | 29 | create_makefile('cbor/cbor') 30 | -------------------------------------------------------------------------------- /ext/cbor/packer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "packer.h" 29 | 30 | #ifdef RUBINIUS 31 | static ID s_to_iter; 32 | static ID s_next; 33 | static ID s_key; 34 | static ID s_value; 35 | #endif 36 | 37 | void msgpack_packer_static_init() 38 | { 39 | #ifdef RUBINIUS 40 | s_to_iter = rb_intern("to_iter"); 41 | s_next = rb_intern("next"); 42 | s_key = rb_intern("key"); 43 | s_value = rb_intern("value"); 44 | #endif 45 | } 46 | 47 | void msgpack_packer_static_destroy() 48 | { } 49 | 50 | void msgpack_packer_init(msgpack_packer_t* pk) 51 | { 52 | memset(pk, 0, sizeof(msgpack_packer_t)); 53 | 54 | msgpack_buffer_init(PACKER_BUFFER_(pk)); 55 | 56 | pk->io = Qnil; 57 | } 58 | 59 | void msgpack_packer_destroy(msgpack_packer_t* pk) 60 | { 61 | msgpack_buffer_destroy(PACKER_BUFFER_(pk)); 62 | } 63 | 64 | void msgpack_packer_mark(msgpack_packer_t* pk) 65 | { 66 | rb_gc_mark(pk->io); 67 | 68 | /* See MessagePack_Buffer_wrap */ 69 | /* msgpack_buffer_mark(PACKER_BUFFER_(pk)); */ 70 | rb_gc_mark(pk->buffer_ref); 71 | } 72 | 73 | void msgpack_packer_reset(msgpack_packer_t* pk) 74 | { 75 | msgpack_buffer_clear(PACKER_BUFFER_(pk)); 76 | 77 | pk->io = Qnil; 78 | pk->io_write_all_method = 0; 79 | pk->buffer_ref = Qnil; 80 | } 81 | 82 | 83 | void msgpack_packer_write_array_value(msgpack_packer_t* pk, VALUE v) 84 | { 85 | /* actual return type of RARRAY_LEN is long */ 86 | unsigned long len = RARRAY_LEN(v); 87 | msgpack_packer_write_array_header(pk, len); 88 | 89 | unsigned long i; 90 | for(i=0; i < len; ++i) { 91 | VALUE e = rb_ary_entry(v, i); 92 | msgpack_packer_write_value(pk, e); 93 | } 94 | } 95 | 96 | static int write_hash_foreach(VALUE key, VALUE value, VALUE pk_value) 97 | { 98 | if (key == Qundef) { 99 | return ST_CONTINUE; 100 | } 101 | msgpack_packer_t* pk = (msgpack_packer_t*) pk_value; 102 | msgpack_packer_write_value(pk, key); 103 | msgpack_packer_write_value(pk, value); 104 | return ST_CONTINUE; 105 | } 106 | 107 | void msgpack_packer_write_hash_value(msgpack_packer_t* pk, VALUE v) 108 | { 109 | /* actual return type of RHASH_SIZE is long (if SIZEOF_LONG == SIZEOF_VOIDP 110 | * or long long (if SIZEOF_LONG_LONG == SIZEOF_VOIDP. See st.h. */ 111 | unsigned long len = RHASH_SIZE(v); 112 | msgpack_packer_write_map_header(pk, len); 113 | 114 | #ifdef RUBINIUS 115 | VALUE iter = rb_funcall(v, s_to_iter, 0); 116 | VALUE entry = Qnil; 117 | while(RTEST(entry = rb_funcall(iter, s_next, 1, entry))) { 118 | VALUE key = rb_funcall(entry, s_key, 0); 119 | VALUE val = rb_funcall(entry, s_value, 0); 120 | write_hash_foreach(key, val, (VALUE) pk); 121 | } 122 | #else 123 | rb_hash_foreach(v, write_hash_foreach, (VALUE) pk); 124 | #endif 125 | } 126 | 127 | static void _msgpack_packer_write_other_value(msgpack_packer_t* pk, VALUE v) 128 | { 129 | rb_funcall(v, pk->to_msgpack_method, 1, pk->to_msgpack_arg); 130 | } 131 | 132 | void msgpack_packer_write_value(msgpack_packer_t* pk, VALUE v) 133 | { 134 | switch(rb_type(v)) { 135 | case T_NIL: 136 | msgpack_packer_write_nil(pk); 137 | break; 138 | case T_TRUE: 139 | msgpack_packer_write_true(pk); 140 | break; 141 | case T_FALSE: 142 | msgpack_packer_write_false(pk); 143 | break; 144 | case T_FIXNUM: 145 | msgpack_packer_write_fixnum_value(pk, v); 146 | break; 147 | case T_SYMBOL: 148 | msgpack_packer_write_symbol_value(pk, v); 149 | break; 150 | case T_STRING: 151 | msgpack_packer_write_string_value(pk, v); 152 | break; 153 | case T_ARRAY: 154 | msgpack_packer_write_array_value(pk, v); 155 | break; 156 | case T_HASH: 157 | msgpack_packer_write_hash_value(pk, v); 158 | break; 159 | case T_BIGNUM: 160 | msgpack_packer_write_bignum_value(pk, v); 161 | break; 162 | case T_FLOAT: 163 | msgpack_packer_write_float_value(pk, v); 164 | break; 165 | default: 166 | _msgpack_packer_write_other_value(pk, v); 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /ext/cbor/packer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_PACKER_H__ 28 | #define MSGPACK_RUBY_PACKER_H__ 29 | 30 | #include "buffer.h" 31 | 32 | #ifndef MSGPACK_PACKER_IO_FLUSH_THRESHOLD_TO_WRITE_STRING_BODY 33 | #define MSGPACK_PACKER_IO_FLUSH_THRESHOLD_TO_WRITE_STRING_BODY (1024) 34 | #endif 35 | 36 | struct msgpack_packer_t; 37 | typedef struct msgpack_packer_t msgpack_packer_t; 38 | 39 | struct msgpack_packer_t { 40 | msgpack_buffer_t buffer; 41 | 42 | VALUE io; 43 | ID io_write_all_method; 44 | 45 | ID to_msgpack_method; 46 | VALUE to_msgpack_arg; 47 | 48 | VALUE buffer_ref; 49 | }; 50 | 51 | #define PACKER_BUFFER_(pk) (&(pk)->buffer) 52 | 53 | void msgpack_packer_static_init(); 54 | 55 | void msgpack_packer_static_destroy(); 56 | 57 | void msgpack_packer_init(msgpack_packer_t* pk); 58 | 59 | void msgpack_packer_destroy(msgpack_packer_t* pk); 60 | 61 | void msgpack_packer_mark(msgpack_packer_t* pk); 62 | 63 | static inline void msgpack_packer_set_to_msgpack_method(msgpack_packer_t* pk, 64 | ID to_msgpack_method, VALUE to_msgpack_arg) 65 | { 66 | pk->to_msgpack_method = to_msgpack_method; 67 | pk->to_msgpack_arg = to_msgpack_arg; 68 | } 69 | 70 | static inline void msgpack_packer_set_io(msgpack_packer_t* pk, VALUE io, ID io_write_all_method) 71 | { 72 | pk->io = io; 73 | pk->io_write_all_method = io_write_all_method; 74 | } 75 | 76 | void msgpack_packer_reset(msgpack_packer_t* pk); 77 | 78 | 79 | static inline void cbor_encoder_write_head(msgpack_packer_t* pk, unsigned int ib, uint64_t n) 80 | { 81 | if (n < 24) { 82 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); 83 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), ib + (int)n); 84 | } else if (n < 256) { 85 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 3); 86 | msgpack_buffer_write_2(PACKER_BUFFER_(pk), ib + 24, n); 87 | } else if (n < 65536) { 88 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 3); 89 | uint16_t be = _msgpack_be16(n); 90 | msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), ib + 25, (const void*)&be, 2); 91 | } else if (n < 0x100000000LU) { 92 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 5); 93 | uint32_t be = _msgpack_be32(n); 94 | msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), ib + 26, (const void*)&be, 4); 95 | } else { 96 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 9); 97 | uint64_t be = _msgpack_be64(n); 98 | msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), ib + 27, (const void*)&be, 8); 99 | } 100 | } 101 | 102 | static inline void msgpack_packer_write_nil(msgpack_packer_t* pk) 103 | { 104 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); 105 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), IB_NIL); 106 | } 107 | 108 | static inline void msgpack_packer_write_true(msgpack_packer_t* pk) 109 | { 110 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); 111 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), IB_TRUE); 112 | } 113 | 114 | static inline void msgpack_packer_write_false(msgpack_packer_t* pk) 115 | { 116 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); 117 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), IB_FALSE); 118 | } 119 | 120 | static inline void _msgpack_packer_write_long_long64(msgpack_packer_t* pk, long long v) 121 | { 122 | uint64_t ui = v >> 63; // extend sign to whole length 123 | int mt = ui & IB_NEGFLAG; // extract major type 124 | ui ^= v; // complement negatives 125 | cbor_encoder_write_head(pk, mt, ui); 126 | } 127 | 128 | static inline void msgpack_packer_write_long(msgpack_packer_t* pk, long v) 129 | { 130 | _msgpack_packer_write_long_long64(pk, v); 131 | } 132 | 133 | static inline void msgpack_packer_write_u64(msgpack_packer_t* pk, uint64_t v) 134 | { 135 | cbor_encoder_write_head(pk, IB_UNSIGNED, v); 136 | } 137 | 138 | static inline void msgpack_packer_write_double(msgpack_packer_t* pk, double v) 139 | { 140 | float fv = v; 141 | if (fv == v) { /* 32 bits is enough and we aren't NaN */ 142 | union { 143 | float f; 144 | uint32_t u32; 145 | char mem[4]; 146 | } castbuf = { fv }; 147 | int b32 = castbuf.u32; 148 | if ((b32 & 0x1FFF) == 0) { /* worth trying half */ 149 | int s16 = (b32 >> 16) & 0x8000; 150 | int exp = (b32 >> 23) & 0xff; 151 | int mant = b32 & 0x7fffff; 152 | if (exp == 0 && mant == 0) 153 | ; /* 0.0, -0.0 */ 154 | else if (exp >= 113 && exp <= 142) /* normalized */ 155 | s16 += ((exp - 112) << 10) + (mant >> 13); 156 | else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */ 157 | if (mant & ((1 << (126 - exp)) - 1)) 158 | goto float32; /* loss of precision */ 159 | s16 += ((mant + 0x800000) >> (126 - exp)); 160 | } else if (exp == 255 && mant == 0) { /* Inf */ 161 | s16 += 0x7c00; 162 | } else 163 | goto float32; /* loss of range */ 164 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 3); 165 | uint16_t be = _msgpack_be16(s16); 166 | msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), IB_FLOAT2, (const void*)&be, 2); 167 | return; 168 | } 169 | float32: 170 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 5); 171 | castbuf.u32 = _msgpack_be_float(castbuf.u32); 172 | msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), IB_FLOAT4, castbuf.mem, 4); 173 | } else if (v != v) { /* NaN */ 174 | cbor_encoder_write_head(pk, 0xe0, 0x7e00); 175 | } else { 176 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 9); 177 | union { 178 | double d; 179 | uint64_t u64; 180 | char mem[8]; 181 | } castbuf = { v }; 182 | castbuf.u64 = _msgpack_be_double(castbuf.u64); 183 | msgpack_buffer_write_byte_and_data(PACKER_BUFFER_(pk), IB_FLOAT8, castbuf.mem, 8); 184 | } 185 | } 186 | 187 | static inline void msgpack_packer_write_array_header(msgpack_packer_t* pk, uint64_t n) 188 | { 189 | cbor_encoder_write_head(pk, IB_ARRAY, n); 190 | } 191 | 192 | static inline void msgpack_packer_write_map_header(msgpack_packer_t* pk, uint64_t n) 193 | { 194 | cbor_encoder_write_head(pk, IB_MAP, n); 195 | } 196 | 197 | 198 | void _msgpack_packer_write_string_to_io(msgpack_packer_t* pk, VALUE string); 199 | 200 | static inline void msgpack_packer_write_string_value(msgpack_packer_t* pk, VALUE v) 201 | { 202 | int mt = IB_TEXT; /* text string */ 203 | #ifdef COMPAT_HAVE_ENCODING 204 | int enc = ENCODING_GET(v); 205 | if (enc == s_enc_ascii8bit) { 206 | mt = IB_BYTES; 207 | } else if (enc != s_enc_utf8 && enc != s_enc_usascii) { 208 | if(!ENC_CODERANGE_ASCIIONLY(v)) { 209 | v = rb_str_encode(v, s_enc_utf8_value, 0, Qnil); 210 | } 211 | } 212 | #endif 213 | cbor_encoder_write_head(pk, mt, RSTRING_LEN(v)); 214 | msgpack_buffer_append_string(PACKER_BUFFER_(pk), v); 215 | } 216 | 217 | static inline void msgpack_packer_write_symbol_value(msgpack_packer_t* pk, VALUE v) 218 | { 219 | #ifdef HAVE_RB_SYM2STR 220 | /* rb_sym2str is added since MRI 2.2.0 */ 221 | msgpack_packer_write_string_value(pk, rb_sym2str(v)); 222 | #else 223 | const char* name = rb_id2name(SYM2ID(v)); 224 | /* actual return type of strlen is size_t */ 225 | unsigned long len = strlen(name); 226 | if(len > 0xffffffffUL) { 227 | // TODO rb_eArgError? 228 | rb_raise(rb_eArgError, "size of symbol is too long to pack: %lu bytes should be <= %lu", len, 0xffffffffUL); 229 | } 230 | cbor_encoder_write_head(pk, IB_TEXT, len); 231 | msgpack_buffer_append(PACKER_BUFFER_(pk), name, len); 232 | #endif 233 | } 234 | 235 | static inline void msgpack_packer_write_fixnum_value(msgpack_packer_t* pk, VALUE v) 236 | { 237 | #ifdef JRUBY 238 | msgpack_packer_write_long(pk, FIXNUM_P(v) ? FIX2LONG(v) : rb_num2ll(v)); 239 | #else 240 | msgpack_packer_write_long(pk, FIX2LONG(v)); 241 | #endif 242 | } 243 | 244 | static inline void msgpack_packer_write_bignum_value(msgpack_packer_t* pk, VALUE v) 245 | { 246 | long len; 247 | int ib = IB_UNSIGNED; 248 | if (!RBIGNUM_POSITIVE_P(v)) { 249 | v = rb_funcall(v, rb_intern("~"), 0); /* should be rb_big_neg(), but that is static. */ 250 | ib = IB_NEGATIVE; 251 | } 252 | 253 | 254 | #ifdef HAVE_RB_INTEGER_UNPACK 255 | len = rb_absint_size(v, NULL); 256 | 257 | if (len > SIZEOF_LONG_LONG) { /* i.e., need real bignum */ 258 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); 259 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), IB_BIGNUM + IB_NEGFLAG_AS_BIT(ib)); 260 | cbor_encoder_write_head(pk, IB_BYTES, len); 261 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), len); 262 | 263 | char buf[len]; /* XXX */ 264 | if (rb_integer_pack(v, buf, len, 1, 0, INTEGER_PACK_BIG_ENDIAN) != 1) 265 | rb_raise(rb_eRangeError, "cbor rb_integer_pack() error"); 266 | 267 | msgpack_buffer_append(PACKER_BUFFER_(pk), buf, len); 268 | 269 | #else 270 | 271 | len = RBIGNUM_LEN(v); 272 | if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS) { 273 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), 1); 274 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), IB_BIGNUM + IB_NEGFLAG_AS_BIT(ib)); 275 | { 276 | #ifndef CANT_DO_BIGNUMS_FAST_ON_THIS_PLATFORM 277 | BDIGIT *dp = RBIGNUM_DIGITS(v); 278 | BDIGIT *de = dp + len; 279 | int nbyte = (len - 1) * SIZEOF_BDIGITS; 280 | int nbmsdig = 0; 281 | BDIGIT msdig; 282 | int i; 283 | if ((msdig = de[-1]) == 0) /* todo: check whether that occurs */ 284 | rb_raise(rb_eRangeError, "cbor writing unnormalized bignum"); 285 | while (msdig) { /* get number of significant bytes in msdig */ 286 | nbmsdig++; 287 | msdig >>= 8; 288 | } 289 | nbyte += nbmsdig; 290 | cbor_encoder_write_head(pk, IB_BYTES, nbyte); 291 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), nbyte); 292 | /* first digit: */ 293 | msdig = de[-1]; 294 | while (nbmsdig) { 295 | --nbmsdig; 296 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), msdig >> (nbmsdig << 3)); 297 | } 298 | /* rest of the digits: */ 299 | for (i = 1; i < len; ++i) { 300 | BDIGIT be = NTOHBDIGIT(de[-1-i]); 301 | msgpack_buffer_append(PACKER_BUFFER_(pk), (const void*)&be, SIZEOF_BDIGITS); 302 | } 303 | #else 304 | /* This is a slow workaround only... But a working one.*/ 305 | size_t nbyte; 306 | VALUE hexval = rb_funcall(v, rb_intern("to_s"), 1, INT2FIX(16)); 307 | char *hp; 308 | int i; 309 | if (RSTRING_LEN(hexval) & 1) 310 | rb_funcall(hexval, rb_intern("[]="), 3, INT2FIX(0), INT2FIX(0), rb_str_new("0", 1)); 311 | nbyte = RSTRING_LEN(hexval) >> 1; 312 | hp = RSTRING_PTR(hexval); 313 | cbor_encoder_write_head(pk, IB_BYTES, nbyte); 314 | msgpack_buffer_ensure_writable(PACKER_BUFFER_(pk), nbyte); 315 | for (i = 0; i < nbyte; i++) { 316 | int c; 317 | sscanf(hp, "%2x", &c); 318 | hp += 2; 319 | msgpack_buffer_write_1(PACKER_BUFFER_(pk), c); 320 | } 321 | #endif 322 | } 323 | #endif 324 | } else { 325 | cbor_encoder_write_head(pk, ib, rb_big2ull(v)); 326 | } 327 | 328 | #ifdef RB_GC_GUARD 329 | RB_GC_GUARD(v); 330 | #endif 331 | } 332 | 333 | static inline void msgpack_packer_write_float_value(msgpack_packer_t* pk, VALUE v) 334 | { 335 | msgpack_packer_write_double(pk, rb_num2dbl(v)); 336 | } 337 | 338 | static inline void msgpack_packer_write_simple_value(msgpack_packer_t* pk, VALUE v) 339 | { 340 | cbor_encoder_write_head(pk, IB_PRIM, FIX2LONG(rb_struct_aref(v, INT2FIX(0)))); 341 | } 342 | 343 | void msgpack_packer_write_array_value(msgpack_packer_t* pk, VALUE v); 344 | 345 | void msgpack_packer_write_hash_value(msgpack_packer_t* pk, VALUE v); 346 | 347 | void msgpack_packer_write_value(msgpack_packer_t* pk, VALUE v); 348 | 349 | static inline void msgpack_packer_write_tagged_value(msgpack_packer_t* pk, VALUE v) 350 | { 351 | cbor_encoder_write_head(pk, IB_TAG, rb_num2ulong(rb_struct_aref(v, INT2FIX(0)))); 352 | msgpack_packer_write_value(pk, rb_struct_aref(v, INT2FIX(1))); 353 | } 354 | 355 | static inline void msgpack_packer_write_processed_value(msgpack_packer_t* pk, VALUE v, ID method, int tag) 356 | { 357 | cbor_encoder_write_head(pk, IB_TAG, tag); 358 | msgpack_packer_write_value(pk, rb_funcall(v, method, 0)); 359 | } 360 | 361 | #endif 362 | 363 | -------------------------------------------------------------------------------- /ext/cbor/packer_class.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "compat.h" 29 | #include "ruby.h" 30 | #include "packer.h" 31 | #include "packer_class.h" 32 | #include "buffer_class.h" 33 | 34 | VALUE cMessagePack_Packer; 35 | 36 | static ID s_to_msgpack; 37 | static ID s_write; 38 | 39 | //static VALUE s_packer_value; 40 | //static msgpack_packer_t* s_packer; 41 | 42 | #define PACKER(from, name) \ 43 | msgpack_packer_t* name; \ 44 | Data_Get_Struct(from, msgpack_packer_t, name); \ 45 | if(name == NULL) { \ 46 | rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \ 47 | } 48 | 49 | static void Packer_free(msgpack_packer_t* pk) 50 | { 51 | if(pk == NULL) { 52 | return; 53 | } 54 | msgpack_packer_destroy(pk); 55 | xfree(pk); 56 | } 57 | 58 | static VALUE Packer_alloc(VALUE klass) 59 | { 60 | msgpack_packer_t* pk = ALLOC_N(msgpack_packer_t, 1); 61 | msgpack_packer_init(pk); 62 | 63 | VALUE self = Data_Wrap_Struct(klass, msgpack_packer_mark, Packer_free, pk); 64 | 65 | msgpack_packer_set_to_msgpack_method(pk, s_to_msgpack, self); 66 | pk->buffer_ref = MessagePack_Buffer_wrap(PACKER_BUFFER_(pk), self); 67 | 68 | return self; 69 | } 70 | 71 | static VALUE Packer_initialize(int argc, VALUE* argv, VALUE self) 72 | { 73 | VALUE io = Qnil; 74 | VALUE options = Qnil; 75 | 76 | if(argc == 0 || (argc == 1 && argv[0] == Qnil)) { 77 | /* Qnil */ 78 | 79 | } else if(argc == 1) { 80 | VALUE v = argv[0]; 81 | if(rb_type(v) == T_HASH) { 82 | options = v; 83 | } else { 84 | io = v; 85 | } 86 | 87 | } else if(argc == 2) { 88 | io = argv[0]; 89 | options = argv[1]; 90 | if(rb_type(options) != T_HASH) { 91 | rb_raise(rb_eArgError, "expected Hash but found %s.", rb_obj_classname(io)); 92 | } 93 | 94 | } else { 95 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc); 96 | } 97 | 98 | PACKER(self, pk); 99 | if(io != Qnil || options != Qnil) { 100 | MessagePack_Buffer_initialize(PACKER_BUFFER_(pk), io, options); 101 | } 102 | 103 | // TODO options 104 | 105 | return self; 106 | } 107 | 108 | static VALUE Packer_buffer(VALUE self) 109 | { 110 | PACKER(self, pk); 111 | return pk->buffer_ref; 112 | } 113 | 114 | static VALUE Packer_write(VALUE self, VALUE v) 115 | { 116 | PACKER(self, pk); 117 | msgpack_packer_write_value(pk, v); 118 | return self; 119 | } 120 | 121 | static VALUE Packer_write_nil(VALUE self) 122 | { 123 | PACKER(self, pk); 124 | msgpack_packer_write_nil(pk); 125 | return self; 126 | } 127 | 128 | static VALUE Packer_write_array_header(VALUE self, VALUE n) 129 | { 130 | PACKER(self, pk); 131 | msgpack_packer_write_array_header(pk, NUM2UINT(n)); 132 | return self; 133 | } 134 | 135 | static VALUE Packer_write_map_header(VALUE self, VALUE n) 136 | { 137 | PACKER(self, pk); 138 | msgpack_packer_write_map_header(pk, NUM2UINT(n)); 139 | return self; 140 | } 141 | 142 | static VALUE Packer_flush(VALUE self) 143 | { 144 | PACKER(self, pk); 145 | msgpack_buffer_flush(PACKER_BUFFER_(pk)); 146 | return self; 147 | } 148 | 149 | static VALUE Packer_clear(VALUE self) 150 | { 151 | PACKER(self, pk); 152 | msgpack_buffer_clear(PACKER_BUFFER_(pk)); 153 | return Qnil; 154 | } 155 | 156 | static VALUE Packer_size(VALUE self) 157 | { 158 | PACKER(self, pk); 159 | size_t size = msgpack_buffer_all_readable_size(PACKER_BUFFER_(pk)); 160 | return SIZET2NUM(size); 161 | } 162 | 163 | static VALUE Packer_empty_p(VALUE self) 164 | { 165 | PACKER(self, pk); 166 | if(msgpack_buffer_top_readable_size(PACKER_BUFFER_(pk)) == 0) { 167 | return Qtrue; 168 | } else { 169 | return Qfalse; 170 | } 171 | } 172 | 173 | static VALUE Packer_to_str(VALUE self) 174 | { 175 | PACKER(self, pk); 176 | return msgpack_buffer_all_as_string(PACKER_BUFFER_(pk)); 177 | } 178 | 179 | static VALUE Packer_to_a(VALUE self) 180 | { 181 | PACKER(self, pk); 182 | return msgpack_buffer_all_as_string_array(PACKER_BUFFER_(pk)); 183 | } 184 | 185 | static VALUE Packer_write_to(VALUE self, VALUE io) 186 | { 187 | PACKER(self, pk); 188 | size_t sz = msgpack_buffer_flush_to_io(PACKER_BUFFER_(pk), io, s_write, true); 189 | return ULONG2NUM(sz); 190 | } 191 | 192 | //static VALUE Packer_append(VALUE self, VALUE string_or_buffer) 193 | //{ 194 | // PACKER(self, pk); 195 | // 196 | // // TODO if string_or_buffer is a Buffer 197 | // VALUE string = string_or_buffer; 198 | // 199 | // msgpack_buffer_append_string(PACKER_BUFFER_(pk), string); 200 | // 201 | // return self; 202 | //} 203 | 204 | VALUE MessagePack_pack(int argc, VALUE* argv) 205 | { 206 | // TODO options 207 | 208 | VALUE v; 209 | VALUE io = Qnil; 210 | 211 | switch(argc) { 212 | case 2: 213 | io = argv[1]; 214 | /* pass-through */ 215 | case 1: 216 | v = argv[0]; 217 | break; 218 | default: 219 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..2)", argc); 220 | } 221 | 222 | VALUE self = Packer_alloc(cMessagePack_Packer); 223 | PACKER(self, pk); 224 | //msgpack_packer_reset(s_packer); 225 | //msgpack_buffer_reset_io(PACKER_BUFFER_(s_packer)); 226 | 227 | if(io != Qnil) { 228 | MessagePack_Buffer_initialize(PACKER_BUFFER_(pk), io, Qnil); 229 | } 230 | 231 | msgpack_packer_write_value(pk, v); 232 | 233 | VALUE retval; 234 | if(io != Qnil) { 235 | msgpack_buffer_flush(PACKER_BUFFER_(pk)); 236 | retval = Qnil; 237 | } else { 238 | retval = msgpack_buffer_all_as_string(PACKER_BUFFER_(pk)); 239 | } 240 | 241 | msgpack_buffer_clear(PACKER_BUFFER_(pk)); /* to free rmem before GC */ 242 | 243 | #ifdef RB_GC_GUARD 244 | /* This prevents compilers from optimizing out the `self` variable 245 | * from stack. Otherwise GC free()s it. */ 246 | RB_GC_GUARD(self); 247 | #endif 248 | 249 | return retval; 250 | } 251 | 252 | static VALUE MessagePack_dump_module_method(int argc, VALUE* argv, VALUE mod) 253 | { 254 | UNUSED(mod); 255 | return MessagePack_pack(argc, argv); 256 | } 257 | 258 | static VALUE MessagePack_pack_module_method(int argc, VALUE* argv, VALUE mod) 259 | { 260 | UNUSED(mod); 261 | return MessagePack_pack(argc, argv); 262 | } 263 | 264 | void MessagePack_Packer_module_init(VALUE mMessagePack) 265 | { 266 | s_to_msgpack = rb_intern("to_cbor"); 267 | s_write = rb_intern("write"); 268 | 269 | msgpack_packer_static_init(); 270 | 271 | cMessagePack_Packer = rb_define_class_under(mMessagePack, "Packer", rb_cObject); 272 | 273 | rb_define_alloc_func(cMessagePack_Packer, Packer_alloc); 274 | 275 | rb_define_method(cMessagePack_Packer, "initialize", Packer_initialize, -1); 276 | rb_define_method(cMessagePack_Packer, "buffer", Packer_buffer, 0); 277 | rb_define_method(cMessagePack_Packer, "write", Packer_write, 1); 278 | rb_define_alias(cMessagePack_Packer, "pack", "write"); 279 | rb_define_method(cMessagePack_Packer, "write_nil", Packer_write_nil, 0); 280 | rb_define_method(cMessagePack_Packer, "write_array_header", Packer_write_array_header, 1); 281 | rb_define_method(cMessagePack_Packer, "write_map_header", Packer_write_map_header, 1); 282 | rb_define_method(cMessagePack_Packer, "flush", Packer_flush, 0); 283 | 284 | /* delegation methods */ 285 | rb_define_method(cMessagePack_Packer, "clear", Packer_clear, 0); 286 | rb_define_method(cMessagePack_Packer, "size", Packer_size, 0); 287 | rb_define_method(cMessagePack_Packer, "empty?", Packer_empty_p, 0); 288 | rb_define_method(cMessagePack_Packer, "write_to", Packer_write_to, 1); 289 | rb_define_method(cMessagePack_Packer, "to_str", Packer_to_str, 0); 290 | rb_define_alias(cMessagePack_Packer, "to_s", "to_str"); 291 | rb_define_method(cMessagePack_Packer, "to_a", Packer_to_a, 0); 292 | //rb_define_method(cMessagePack_Packer, "append", Packer_append, 1); 293 | //rb_define_alias(cMessagePack_Packer, "<<", "append"); 294 | 295 | //s_packer_value = Packer_alloc(cMessagePack_Packer); 296 | //rb_gc_register_address(&s_packer_value); 297 | //Data_Get_Struct(s_packer_value, msgpack_packer_t, s_packer); 298 | 299 | /* MessagePack.pack(x) */ 300 | rb_define_module_function(mMessagePack, "pack", MessagePack_pack_module_method, -1); 301 | rb_define_module_function(mMessagePack, "encode", MessagePack_pack_module_method, -1); 302 | rb_define_module_function(mMessagePack, "dump", MessagePack_dump_module_method, -1); 303 | } 304 | 305 | -------------------------------------------------------------------------------- /ext/cbor/packer_class.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_PACKER_CLASS_H__ 28 | #define MSGPACK_RUBY_PACKER_CLASS_H__ 29 | 30 | #include "packer.h" 31 | 32 | extern VALUE cMessagePack_Packer; 33 | 34 | void MessagePack_Packer_module_init(VALUE mMessagePack); 35 | 36 | VALUE MessagePack_pack(int argc, VALUE* argv); 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /ext/cbor/rbinit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "buffer_class.h" 29 | #include "packer_class.h" 30 | #include "unpacker_class.h" 31 | #include "core_ext.h" 32 | 33 | 34 | VALUE rb_cCBOR_Tagged; 35 | VALUE rb_cCBOR_Simple; 36 | 37 | void Init_cbor(void) 38 | { 39 | VALUE mMessagePack = rb_define_module("CBOR"); 40 | 41 | rb_cCBOR_Tagged = rb_struct_define(NULL, "tag", "value", NULL); 42 | rb_define_const(mMessagePack, "Tagged", rb_cCBOR_Tagged); 43 | rb_cCBOR_Simple = rb_struct_define(NULL, "value", NULL); 44 | rb_define_const(mMessagePack, "Simple", rb_cCBOR_Simple); 45 | 46 | MessagePack_Buffer_module_init(mMessagePack); 47 | MessagePack_Packer_module_init(mMessagePack); 48 | MessagePack_Unpacker_module_init(mMessagePack); 49 | MessagePack_core_ext_module_init(); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /ext/cbor/renamer.h: -------------------------------------------------------------------------------- 1 | #define MessagePack_Buffer_initialize CBOR_Buffer_initialize 2 | #define MessagePack_Buffer_module_init CBOR_Buffer_module_init 3 | #define MessagePack_Buffer_wrap CBOR_Buffer_wrap 4 | #define MessagePack_Packer_module_init CBOR_Packer_module_init 5 | #define MessagePack_Unpacker_module_init CBOR_Unpacker_module_init 6 | #define MessagePack_core_ext_module_init CBOR_core_ext_module_init 7 | #define MessagePack_pack CBOR_pack 8 | #define MessagePack_unpack CBOR_unpack 9 | #define _msgpack_buffer_append_long_string _CBOR_buffer_append_long_string 10 | #define _msgpack_buffer_expand _CBOR_buffer_expand 11 | #define _msgpack_buffer_feed_from_io _CBOR_buffer_feed_from_io 12 | #define _msgpack_buffer_read_all2 _CBOR_buffer_read_all2 13 | #define _msgpack_buffer_read_from_io_to_string _CBOR_buffer_read_from_io_to_string 14 | #define _msgpack_buffer_shift_chunk _CBOR_buffer_shift_chunk 15 | #define _msgpack_buffer_skip_from_io _CBOR_buffer_skip_from_io 16 | #define _msgpack_rmem_alloc2 _CBOR_rmem_alloc2 17 | #define _msgpack_rmem_chunk_free _CBOR_rmem_chunk_free 18 | #define cMessagePack_Buffer cCBOR_Buffer 19 | #define cMessagePack_Packer cCBOR_Packer 20 | #define cMessagePack_Unpacker cCBOR_Unpacker 21 | #define msgpack_buffer_all_as_string CBOR_buffer_all_as_string 22 | #define msgpack_buffer_all_as_string_array CBOR_buffer_all_as_string_array 23 | #define msgpack_buffer_all_readable_size CBOR_buffer_all_readable_size 24 | #define msgpack_buffer_clear CBOR_buffer_clear 25 | #define msgpack_buffer_destroy CBOR_buffer_destroy 26 | #define msgpack_buffer_flush_to_io CBOR_buffer_flush_to_io 27 | #define msgpack_buffer_init CBOR_buffer_init 28 | #define msgpack_buffer_mark CBOR_buffer_mark 29 | #define msgpack_buffer_read_nonblock CBOR_buffer_read_nonblock 30 | #define msgpack_buffer_read_to_string_nonblock CBOR_buffer_read_to_string_nonblock 31 | #define msgpack_buffer_static_destroy CBOR_buffer_static_destroy 32 | #define msgpack_buffer_static_init CBOR_buffer_static_init 33 | #define msgpack_packer_destroy CBOR_packer_destroy 34 | #define msgpack_packer_init CBOR_packer_init 35 | #define msgpack_packer_mark CBOR_packer_mark 36 | #define msgpack_packer_reset CBOR_packer_reset 37 | #define msgpack_packer_static_destroy CBOR_packer_static_destroy 38 | #define msgpack_packer_static_init CBOR_packer_static_init 39 | #define msgpack_packer_write_array_value CBOR_packer_write_array_value 40 | #define msgpack_packer_write_hash_value CBOR_packer_write_hash_value 41 | #define msgpack_packer_write_value CBOR_packer_write_value 42 | #define msgpack_rmem_destroy CBOR_rmem_destroy 43 | #define msgpack_rmem_init CBOR_rmem_init 44 | #define msgpack_unpacker_destroy CBOR_unpacker_destroy 45 | #define msgpack_unpacker_init CBOR_unpacker_init 46 | #define msgpack_unpacker_mark CBOR_unpacker_mark 47 | #define msgpack_unpacker_peek_next_object_type CBOR_unpacker_peek_next_object_type 48 | #define msgpack_unpacker_read CBOR_unpacker_read 49 | #define msgpack_unpacker_read_array_header CBOR_unpacker_read_array_header 50 | #define msgpack_unpacker_read_container_header CBOR_unpacker_read_container_header 51 | #define msgpack_unpacker_read_map_header CBOR_unpacker_read_map_header 52 | #define msgpack_unpacker_reset CBOR_unpacker_reset 53 | #define msgpack_unpacker_skip CBOR_unpacker_skip 54 | #define msgpack_unpacker_skip_nil CBOR_unpacker_skip_nil 55 | #define msgpack_unpacker_static_destroy CBOR_unpacker_static_destroy 56 | #define msgpack_unpacker_static_init CBOR_unpacker_static_init 57 | -------------------------------------------------------------------------------- /ext/cbor/rmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "rmem.h" 29 | 30 | void msgpack_rmem_init(msgpack_rmem_t* pm) 31 | { 32 | memset(pm, 0, sizeof(msgpack_rmem_t)); 33 | pm->head.pages = malloc(MSGPACK_RMEM_PAGE_SIZE * 32); 34 | pm->head.mask = 0xffffffff; /* all bit is 1 = available */ 35 | } 36 | 37 | void msgpack_rmem_destroy(msgpack_rmem_t* pm) 38 | { 39 | msgpack_rmem_chunk_t* c = pm->array_first; 40 | msgpack_rmem_chunk_t* cend = pm->array_last; 41 | for(; c != cend; c++) { 42 | free(c->pages); 43 | } 44 | free(pm->head.pages); 45 | free(pm->array_first); 46 | } 47 | 48 | void* _msgpack_rmem_alloc2(msgpack_rmem_t* pm) 49 | { 50 | msgpack_rmem_chunk_t* c = pm->array_first; 51 | msgpack_rmem_chunk_t* last = pm->array_last; 52 | for(; c != last; c++) { 53 | if(_msgpack_rmem_chunk_available(c)) { 54 | void* mem = _msgpack_rmem_chunk_alloc(c); 55 | 56 | /* move to head */ 57 | msgpack_rmem_chunk_t tmp = pm->head; 58 | pm->head = *c; 59 | *c = tmp; 60 | return mem; 61 | } 62 | } 63 | 64 | if(c == pm->array_end) { 65 | size_t capacity = c - pm->array_first; 66 | size_t length = last - pm->array_first; 67 | capacity = (capacity == 0) ? 8 : capacity * 2; 68 | msgpack_rmem_chunk_t* array = realloc(pm->array_first, capacity * sizeof(msgpack_rmem_chunk_t)); 69 | pm->array_first = array; 70 | pm->array_last = array + length; 71 | pm->array_end = array + capacity; 72 | } 73 | 74 | /* allocate new chunk */ 75 | c = pm->array_last++; 76 | 77 | /* move to head */ 78 | msgpack_rmem_chunk_t tmp = pm->head; 79 | pm->head = *c; 80 | *c = tmp; 81 | 82 | pm->head.mask = 0xffffffff & (~1); /* "& (~1)" means first chunk is already allocated */ 83 | pm->head.pages = malloc(MSGPACK_RMEM_PAGE_SIZE * 32); 84 | 85 | return pm->head.pages; 86 | } 87 | 88 | void _msgpack_rmem_chunk_free(msgpack_rmem_t* pm, msgpack_rmem_chunk_t* c) 89 | { 90 | if(pm->array_first->mask == 0xffffffff) { 91 | /* free and move to last */ 92 | pm->array_last--; 93 | free(c->pages); 94 | *c = *pm->array_last; 95 | return; 96 | } 97 | 98 | /* move to first */ 99 | msgpack_rmem_chunk_t tmp = *pm->array_first; 100 | *pm->array_first = *c; 101 | *c = tmp; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /ext/cbor/rmem.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_RMEM_H__ 28 | #define MSGPACK_RUBY_RMEM_H__ 29 | 30 | #include "compat.h" 31 | #include "sysdep.h" 32 | 33 | #ifndef MSGPACK_RMEM_PAGE_SIZE 34 | #define MSGPACK_RMEM_PAGE_SIZE (4*1024) 35 | #endif 36 | 37 | struct msgpack_rmem_t; 38 | typedef struct msgpack_rmem_t msgpack_rmem_t; 39 | 40 | struct msgpack_rmem_chunk_t; 41 | typedef struct msgpack_rmem_chunk_t msgpack_rmem_chunk_t; 42 | 43 | /* 44 | * a chunk contains 32 pages. 45 | * size of each buffer is MSGPACK_RMEM_PAGE_SIZE bytes. 46 | */ 47 | struct msgpack_rmem_chunk_t { 48 | unsigned int mask; 49 | char* pages; 50 | }; 51 | 52 | struct msgpack_rmem_t { 53 | msgpack_rmem_chunk_t head; 54 | msgpack_rmem_chunk_t* array_first; 55 | msgpack_rmem_chunk_t* array_last; 56 | msgpack_rmem_chunk_t* array_end; 57 | }; 58 | 59 | /* assert MSGPACK_RMEM_PAGE_SIZE % sysconf(_SC_PAGE_SIZE) == 0 */ 60 | void msgpack_rmem_init(msgpack_rmem_t* pm); 61 | 62 | void msgpack_rmem_destroy(msgpack_rmem_t* pm); 63 | 64 | void* _msgpack_rmem_alloc2(msgpack_rmem_t* pm); 65 | 66 | #define _msgpack_rmem_chunk_available(c) ((c)->mask != 0) 67 | 68 | static inline void* _msgpack_rmem_chunk_alloc(msgpack_rmem_chunk_t* c) 69 | { 70 | _msgpack_bsp32(pos, c->mask); 71 | (c)->mask &= ~(1 << pos); 72 | return ((char*)(c)->pages) + (pos * (MSGPACK_RMEM_PAGE_SIZE)); 73 | } 74 | 75 | static inline bool _msgpack_rmem_chunk_try_free(msgpack_rmem_chunk_t* c, void* mem) 76 | { 77 | ptrdiff_t pdiff = ((char*)(mem)) - ((char*)(c)->pages); 78 | if(0 <= pdiff && pdiff < MSGPACK_RMEM_PAGE_SIZE * 32) { 79 | size_t pos = pdiff / MSGPACK_RMEM_PAGE_SIZE; 80 | (c)->mask |= (1 << pos); 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | static inline void* msgpack_rmem_alloc(msgpack_rmem_t* pm) 87 | { 88 | if(_msgpack_rmem_chunk_available(&pm->head)) { 89 | return _msgpack_rmem_chunk_alloc(&pm->head); 90 | } 91 | return _msgpack_rmem_alloc2(pm); 92 | } 93 | 94 | void _msgpack_rmem_chunk_free(msgpack_rmem_t* pm, msgpack_rmem_chunk_t* c); 95 | 96 | static inline bool msgpack_rmem_free(msgpack_rmem_t* pm, void* mem) 97 | { 98 | if(_msgpack_rmem_chunk_try_free(&pm->head, mem)) { 99 | return true; 100 | } 101 | 102 | /* search from last */ 103 | msgpack_rmem_chunk_t* c = pm->array_last - 1; 104 | msgpack_rmem_chunk_t* before_first = pm->array_first - 1; 105 | for(; c != before_first; c--) { 106 | if(_msgpack_rmem_chunk_try_free(c, mem)) { 107 | if(c != pm->array_first && c->mask == 0xffffffff) { 108 | _msgpack_rmem_chunk_free(pm, c); 109 | } 110 | return true; 111 | } 112 | } 113 | return false; 114 | } 115 | 116 | 117 | #endif 118 | 119 | -------------------------------------------------------------------------------- /ext/cbor/sysdep.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_SYSDEP_H__ 28 | #define MSGPACK_RUBY_SYSDEP_H__ 29 | 30 | #include "renamer.h" 31 | 32 | #include "sysdep_types.h" 33 | #include "sysdep_endian.h" 34 | 35 | 36 | #define UNUSED(var) ((void)var) 37 | 38 | 39 | #ifdef __LITTLE_ENDIAN__ 40 | 41 | /* _msgpack_be16 */ 42 | #ifdef _WIN32 43 | # if defined(ntohs) 44 | # define _msgpack_be16(x) ntohs(x) 45 | # elif defined(_byteswap_ushort) || (defined(_MSC_VER) && _MSC_VER >= 1400) 46 | # define _msgpack_be16(x) ((uint16_t)_byteswap_ushort((unsigned short)x)) 47 | # else 48 | # define _msgpack_be16(x) ( \ 49 | ((((uint16_t)x) << 8) & 0x0000ff00U ) | \ 50 | ((((uint16_t)x) >> 8) & 0x000000ffU ) ) 51 | # endif 52 | #else 53 | # define _msgpack_be16(x) ntohs(x) 54 | #endif 55 | 56 | /* _msgpack_be32 */ 57 | #ifdef _WIN32 58 | # if defined(ntohl) 59 | # define _msgpack_be32(x) ntohl(x) 60 | # elif defined(_byteswap_ulong) || (defined(_MSC_VER) && _MSC_VER >= 1400) 61 | # define _msgpack_be32(x) ((uint32_t)_byteswap_ulong((unsigned long)x)) 62 | # else 63 | # define _msgpack_be32(x) \ 64 | ( ((((uint32_t)x) << 24) ) | \ 65 | ((((uint32_t)x) << 8) & 0x00ff0000U ) | \ 66 | ((((uint32_t)x) >> 8) & 0x0000ff00U ) | \ 67 | ((((uint32_t)x) >> 24) ) ) 68 | # endif 69 | #else 70 | # define _msgpack_be32(x) ntohl(x) 71 | #endif 72 | 73 | /* _msgpack_be64 */ 74 | #if defined(_byteswap_uint64) || (defined(_MSC_VER) && _MSC_VER >= 1400) 75 | # define _msgpack_be64(x) (_byteswap_uint64(x)) 76 | #elif defined(bswap_64) 77 | # define _msgpack_be64(x) bswap_64(x) 78 | #elif defined(__DARWIN_OSSwapInt64) 79 | # define _msgpack_be64(x) __DARWIN_OSSwapInt64(x) 80 | #else 81 | #define _msgpack_be64(x) \ 82 | ( ((((uint64_t)x) << 56) ) | \ 83 | ((((uint64_t)x) << 40) & 0x00ff000000000000ULL ) | \ 84 | ((((uint64_t)x) << 24) & 0x0000ff0000000000ULL ) | \ 85 | ((((uint64_t)x) << 8) & 0x000000ff00000000ULL ) | \ 86 | ((((uint64_t)x) >> 8) & 0x00000000ff000000ULL ) | \ 87 | ((((uint64_t)x) >> 24) & 0x0000000000ff0000ULL ) | \ 88 | ((((uint64_t)x) >> 40) & 0x000000000000ff00ULL ) | \ 89 | ((((uint64_t)x) >> 56) ) ) 90 | #endif 91 | 92 | #else /* big endian */ 93 | #define _msgpack_be16(x) (x) 94 | #define _msgpack_be32(x) (x) 95 | #define _msgpack_be64(x) (x) 96 | 97 | #endif 98 | 99 | 100 | /* _msgpack_be_float */ 101 | #define _msgpack_be_float(x) _msgpack_be32(x) 102 | 103 | /* _msgpack_be_double */ 104 | #if defined(__arm__) && !(__ARM_EABI__) 105 | /* ARM OABI */ 106 | #define _msgpack_be_double(x) \ 107 | ( (((x) & 0xFFFFFFFFUL) << 32UL) | ((x) >> 32UL) ) 108 | #else 109 | /* the other ABI */ 110 | #define _msgpack_be_double(x) _msgpack_be64(x) 111 | #endif 112 | 113 | /* _msgpack_bsp32 */ 114 | #if defined(_MSC_VER) 115 | #define _msgpack_bsp32(name, val) \ 116 | long name; \ 117 | _BitScanForward(&name, val) 118 | #else 119 | #define _msgpack_bsp32(name, val) \ 120 | int name = __builtin_ctz(val) 121 | /* TODO default impl for _msgpack_bsp32 */ 122 | #endif 123 | 124 | #ifndef HAVE_RB_INTEGER_UNPACK 125 | 126 | #if SIZEOF_BDIGITS == 2 127 | #define NTOHBDIGIT _msgpack_be16 128 | #elif SIZEOF_BDIGITS == 4 129 | #define NTOHBDIGIT _msgpack_be32 130 | #elif SIZEOF_BDIGITS == 8 131 | #define NTOHBDIGIT _msgpack_be64 132 | #else 133 | #error this size of bignum digits SIZEOF_BDIGITS not implemented 134 | #endif 135 | 136 | #endif 137 | 138 | #endif 139 | 140 | -------------------------------------------------------------------------------- /ext/cbor/sysdep_endian.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_SYSDEP_ENDIAN_H__ 28 | #define MSGPACK_RUBY_SYSDEP_ENDIAN_H__ 29 | 30 | /* including arpa/inet.h requires an extra dll on win32 */ 31 | #ifndef _WIN32 32 | #include /* __BYTE_ORDER */ 33 | #endif 34 | 35 | /* 36 | * Use following command to add consitions here: 37 | * cpp -dM `echo "#include " > test.c; echo test.c` | grep ENDIAN 38 | */ 39 | #if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) /* Mac OS X */ 40 | # if defined(_LITTLE_ENDIAN) \ 41 | || ( defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) \ 42 | && __BYTE_ORDER == __LITTLE_ENDIAN ) /* Linux */ \ 43 | || ( defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) \ 44 | && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) /* Solaris */ 45 | # define __LITTLE_ENDIAN__ 46 | # elif defined(_BIG_ENDIAN) \ 47 | || (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) \ 48 | && __BYTE_ORDER == __BIG_ENDIAN) /* Linux */ \ 49 | || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) \ 50 | && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) /* Solaris */ 51 | # define __BIG_ENDIAN__ 52 | # elif defined(_WIN32) /* Win32 */ 53 | # define __LITTLE_ENDIAN__ 54 | # endif 55 | #endif 56 | 57 | 58 | #endif 59 | 60 | -------------------------------------------------------------------------------- /ext/cbor/sysdep_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_SYSDEP_TYPES_H__ 28 | #define MSGPACK_RUBY_SYSDEP_TYPES_H__ 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #if defined(_MSC_VER) && _MSC_VER < 1600 36 | typedef __int8 int8_t; 37 | typedef unsigned __int8 uint8_t; 38 | typedef __int16 int16_t; 39 | typedef unsigned __int16 uint16_t; 40 | typedef __int32 int32_t; 41 | typedef unsigned __int32 uint32_t; 42 | typedef __int64 int64_t; 43 | typedef unsigned __int64 uint64_t; 44 | 45 | #elif defined(_MSC_VER) // && _MSC_VER >= 1600 46 | #include 47 | 48 | #else 49 | #include 50 | #include 51 | #endif 52 | 53 | 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /ext/cbor/unpacker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_UNPACKER_H__ 28 | #define MSGPACK_RUBY_UNPACKER_H__ 29 | 30 | #include "buffer.h" 31 | 32 | #ifndef MSGPACK_UNPACKER_STACK_CAPACITY 33 | #define MSGPACK_UNPACKER_STACK_CAPACITY 128 34 | #endif 35 | 36 | struct msgpack_unpacker_t; 37 | typedef struct msgpack_unpacker_t msgpack_unpacker_t; 38 | 39 | enum stack_type_t { 40 | STACK_TYPE_ARRAY, 41 | STACK_TYPE_MAP_KEY, 42 | STACK_TYPE_MAP_VALUE, 43 | STACK_TYPE_TAG, /* > TAG = indef */ 44 | STACK_TYPE_MAP_VALUE_INDEF, /* Cannot BREAK up to here */ 45 | STACK_TYPE_MAP_KEY_INDEF, 46 | STACK_TYPE_ARRAY_INDEF, 47 | STACK_TYPE_STRING_INDEF, 48 | }; 49 | 50 | typedef struct { 51 | size_t count; 52 | enum stack_type_t type; 53 | VALUE object; 54 | VALUE key; 55 | uint64_t tag; /* could be union... */ 56 | } msgpack_unpacker_stack_t; 57 | 58 | #define MSGPACK_UNPACKER_STACK_SIZE (8+4+8+8+8) /* assumes size_t <= 64bit, enum <= 32bit, VALUE <= 64bit */ 59 | 60 | struct msgpack_unpacker_t { 61 | msgpack_buffer_t buffer; 62 | 63 | unsigned int head_byte; 64 | 65 | msgpack_unpacker_stack_t* stack; 66 | size_t stack_depth; 67 | size_t stack_capacity; 68 | 69 | VALUE last_object; 70 | 71 | VALUE reading_raw; 72 | size_t reading_raw_remaining; 73 | int textflag; 74 | 75 | bool keys_as_symbols; /* Experimental */ 76 | 77 | VALUE buffer_ref; 78 | }; 79 | 80 | #define UNPACKER_BUFFER_(uk) (&(uk)->buffer) 81 | 82 | enum msgpack_unpacker_object_type { 83 | TYPE_NIL = 0, 84 | TYPE_BOOLEAN, 85 | TYPE_INTEGER, 86 | TYPE_FLOAT, 87 | TYPE_RAW, 88 | TYPE_ARRAY, 89 | TYPE_MAP, 90 | }; 91 | 92 | void msgpack_unpacker_static_init(); 93 | 94 | void msgpack_unpacker_static_destroy(); 95 | 96 | void msgpack_unpacker_init(msgpack_unpacker_t* uk); 97 | 98 | void msgpack_unpacker_destroy(msgpack_unpacker_t* uk); 99 | 100 | void msgpack_unpacker_mark(msgpack_unpacker_t* uk); 101 | 102 | void msgpack_unpacker_reset(msgpack_unpacker_t* uk); 103 | 104 | 105 | /* error codes */ 106 | #define PRIMITIVE_CONTAINER_START 1 107 | #define PRIMITIVE_OBJECT_COMPLETE 0 108 | #define PRIMITIVE_EOF -1 109 | #define PRIMITIVE_INVALID_BYTE -2 110 | #define PRIMITIVE_STACK_TOO_DEEP -3 111 | #define PRIMITIVE_UNEXPECTED_TYPE -4 112 | #define PRIMITIVE_BREAK 2 113 | 114 | int msgpack_unpacker_read(msgpack_unpacker_t* uk, size_t target_stack_depth); 115 | 116 | int msgpack_unpacker_skip(msgpack_unpacker_t* uk, size_t target_stack_depth); 117 | 118 | static inline VALUE msgpack_unpacker_get_last_object(msgpack_unpacker_t* uk) 119 | { 120 | return uk->last_object; 121 | } 122 | 123 | 124 | int msgpack_unpacker_peek_next_object_type(msgpack_unpacker_t* uk); 125 | 126 | int msgpack_unpacker_skip_nil(msgpack_unpacker_t* uk); 127 | 128 | int msgpack_unpacker_read_array_header(msgpack_unpacker_t* uk, uint64_t* result_size); 129 | 130 | int msgpack_unpacker_read_map_header(msgpack_unpacker_t* uk, uint64_t* result_size); 131 | 132 | #endif 133 | 134 | -------------------------------------------------------------------------------- /ext/cbor/unpacker_class.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | 28 | #include "unpacker.h" 29 | #include "unpacker_class.h" 30 | #include "buffer_class.h" 31 | 32 | VALUE cMessagePack_Unpacker; 33 | 34 | //static VALUE s_unpacker_value; 35 | //static msgpack_unpacker_t* s_unpacker; 36 | 37 | static VALUE eUnpackError; 38 | static VALUE eMalformedFormatError; 39 | static VALUE eStackError; 40 | static VALUE eTypeError; 41 | 42 | #define UNPACKER(from, name) \ 43 | msgpack_unpacker_t *name = NULL; \ 44 | Data_Get_Struct(from, msgpack_unpacker_t, name); \ 45 | if(name == NULL) { \ 46 | rb_raise(rb_eArgError, "NULL found for " # name " when shouldn't be."); \ 47 | } 48 | 49 | static void Unpacker_free(msgpack_unpacker_t* uk) 50 | { 51 | if(uk == NULL) { 52 | return; 53 | } 54 | msgpack_unpacker_destroy(uk); 55 | xfree(uk); 56 | } 57 | 58 | static VALUE Unpacker_alloc(VALUE klass) 59 | { 60 | msgpack_unpacker_t* uk = ALLOC_N(msgpack_unpacker_t, 1); 61 | msgpack_unpacker_init(uk); 62 | 63 | VALUE self = Data_Wrap_Struct(klass, msgpack_unpacker_mark, Unpacker_free, uk); 64 | 65 | uk->buffer_ref = MessagePack_Buffer_wrap(UNPACKER_BUFFER_(uk), self); 66 | 67 | return self; 68 | } 69 | 70 | static VALUE Unpacker_initialize(int argc, VALUE* argv, VALUE self) 71 | { 72 | VALUE io = Qnil; 73 | VALUE options = Qnil; 74 | 75 | if(argc == 0 || (argc == 1 && argv[0] == Qnil)) { 76 | /* Qnil */ 77 | 78 | } else if(argc == 1) { 79 | VALUE v = argv[0]; 80 | if(rb_type(v) == T_HASH) { 81 | options = v; 82 | } else { 83 | io = v; 84 | } 85 | 86 | } else if(argc == 2) { 87 | io = argv[0]; 88 | options = argv[1]; 89 | if(rb_type(options) != T_HASH) { 90 | rb_raise(rb_eArgError, "expected Hash but found %s.", rb_obj_classname(options)); 91 | } 92 | 93 | } else { 94 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc); 95 | } 96 | 97 | UNPACKER(self, uk); 98 | if(io != Qnil || options != Qnil) { 99 | MessagePack_Buffer_initialize(UNPACKER_BUFFER_(uk), io, options); 100 | if (options != Qnil) { 101 | VALUE v; 102 | v = rb_hash_aref(options, ID2SYM(rb_intern("symbolize_keys"))); 103 | uk->keys_as_symbols = RTEST(v); 104 | } 105 | } 106 | 107 | // TODO options 108 | 109 | return self; 110 | } 111 | 112 | static void raise_unpacker_error(int r) 113 | { 114 | switch(r) { 115 | case PRIMITIVE_EOF: 116 | rb_raise(rb_eEOFError, "end of buffer reached"); 117 | case PRIMITIVE_INVALID_BYTE: 118 | rb_raise(eMalformedFormatError, "invalid byte"); 119 | case PRIMITIVE_STACK_TOO_DEEP: 120 | rb_raise(eStackError, "stack level too deep"); 121 | case PRIMITIVE_UNEXPECTED_TYPE: 122 | rb_raise(eTypeError, "unexpected type"); 123 | default: 124 | rb_raise(eUnpackError, "logically unknown error %d", r); 125 | } 126 | } 127 | 128 | static VALUE Unpacker_buffer(VALUE self) 129 | { 130 | UNPACKER(self, uk); 131 | return uk->buffer_ref; 132 | } 133 | 134 | static VALUE Unpacker_read(VALUE self) 135 | { 136 | UNPACKER(self, uk); 137 | 138 | int r = msgpack_unpacker_read(uk, 0); 139 | if(r < 0) { 140 | raise_unpacker_error(r); 141 | } 142 | 143 | return msgpack_unpacker_get_last_object(uk); 144 | } 145 | 146 | static VALUE Unpacker_skip(VALUE self) 147 | { 148 | UNPACKER(self, uk); 149 | 150 | int r = msgpack_unpacker_skip(uk, 0); 151 | if(r < 0) { 152 | raise_unpacker_error(r); 153 | } 154 | 155 | return Qnil; 156 | } 157 | 158 | static VALUE Unpacker_skip_nil(VALUE self) 159 | { 160 | UNPACKER(self, uk); 161 | 162 | int r = msgpack_unpacker_skip_nil(uk); 163 | if(r < 0) { 164 | raise_unpacker_error(r); 165 | } 166 | 167 | if(r) { 168 | return Qtrue; 169 | } 170 | return Qfalse; 171 | } 172 | 173 | static VALUE Unpacker_read_array_header(VALUE self) 174 | { 175 | UNPACKER(self, uk); 176 | 177 | uint64_t size; 178 | int r = msgpack_unpacker_read_array_header(uk, &size); 179 | if(r < 0) { 180 | raise_unpacker_error(r); 181 | } 182 | 183 | return rb_ull2inum(size); 184 | } 185 | 186 | static VALUE Unpacker_read_map_header(VALUE self) 187 | { 188 | UNPACKER(self, uk); 189 | 190 | uint64_t size; 191 | int r = msgpack_unpacker_read_map_header(uk, &size); 192 | if(r < 0) { 193 | raise_unpacker_error((int)r); 194 | } 195 | 196 | return rb_ull2inum(size); 197 | } 198 | 199 | #if 0 200 | static VALUE Unpacker_peek_next_type(VALUE self) 201 | { 202 | UNPACKER(self, uk); 203 | 204 | int r = msgpack_unpacker_peek_next_object_type(uk); 205 | if(r < 0) { 206 | raise_unpacker_error(r); 207 | } 208 | 209 | switch((enum msgpack_unpacker_object_type) r) { 210 | case TYPE_NIL: 211 | return rb_intern("nil"); 212 | case TYPE_BOOLEAN: 213 | return rb_intern("boolean"); 214 | case TYPE_INTEGER: 215 | return rb_intern("integer"); 216 | case TYPE_FLOAT: 217 | return rb_intern("float"); 218 | case TYPE_RAW: 219 | return rb_intern("raw"); 220 | case TYPE_ARRAY: 221 | return rb_intern("array"); 222 | case TYPE_MAP: 223 | return rb_intern("map"); 224 | default: 225 | rb_raise(eUnpackError, "logically unknown type %d", r); 226 | } 227 | } 228 | #endif 229 | 230 | static VALUE Unpacker_feed(VALUE self, VALUE data) 231 | { 232 | UNPACKER(self, uk); 233 | 234 | StringValue(data); 235 | 236 | msgpack_buffer_append_string(UNPACKER_BUFFER_(uk), data); 237 | 238 | return self; 239 | } 240 | 241 | static VALUE Unpacker_each_impl(VALUE self) 242 | { 243 | UNPACKER(self, uk); 244 | 245 | while(true) { 246 | int r = msgpack_unpacker_read(uk, 0); 247 | if(r < 0) { 248 | if(r == PRIMITIVE_EOF) { 249 | return Qnil; 250 | } 251 | raise_unpacker_error(r); 252 | } 253 | VALUE v = msgpack_unpacker_get_last_object(uk); 254 | #ifdef JRUBY 255 | /* TODO JRuby's rb_yield behaves differently from Ruby 1.9.3 or Rubinius. */ 256 | if(rb_type(v) == T_ARRAY) { 257 | v = rb_ary_new3(1, v); 258 | } 259 | #endif 260 | rb_yield(v); 261 | } 262 | } 263 | 264 | static VALUE Unpacker_rescue_EOFError(VALUE self, VALUE error) 265 | { 266 | UNUSED(self); 267 | UNUSED(error); 268 | return Qnil; 269 | } 270 | 271 | static VALUE Unpacker_each(VALUE self) 272 | { 273 | UNPACKER(self, uk); 274 | 275 | #ifdef RETURN_ENUMERATOR 276 | RETURN_ENUMERATOR(self, 0, 0); 277 | #endif 278 | 279 | if(msgpack_buffer_has_io(UNPACKER_BUFFER_(uk))) { 280 | /* rescue EOFError only if io is set */ 281 | return rb_rescue2(Unpacker_each_impl, self, 282 | Unpacker_rescue_EOFError, self, 283 | rb_eEOFError, NULL); 284 | } else { 285 | return Unpacker_each_impl(self); 286 | } 287 | } 288 | 289 | static VALUE Unpacker_feed_each(VALUE self, VALUE data) 290 | { 291 | // TODO optimize 292 | Unpacker_feed(self, data); 293 | return Unpacker_each(self); 294 | } 295 | 296 | static VALUE Unpacker_reset(VALUE self) 297 | { 298 | UNPACKER(self, uk); 299 | 300 | msgpack_unpacker_reset(uk); 301 | 302 | return Qnil; 303 | } 304 | 305 | VALUE MessagePack_unpack(int argc, VALUE* argv) 306 | { 307 | VALUE src; 308 | VALUE options = Qnil; 309 | bool keys_as_symbols = false; 310 | 311 | switch(argc) { 312 | case 2: 313 | options = argv[1]; /* Experimental! */ 314 | if (options == ID2SYM(rb_intern("keys_as_symbols"))) /* backward compat */ 315 | keys_as_symbols = true; 316 | else if (options != Qnil) { 317 | VALUE v; 318 | if (!RB_TYPE_P(options, T_HASH)) { 319 | rb_raise(rb_eArgError, "expected Hash but found %s.", rb_obj_classname(options)); 320 | } 321 | v = rb_hash_aref(options, ID2SYM(rb_intern("symbolize_keys"))); 322 | keys_as_symbols = RTEST(v); 323 | } 324 | /* fall through */ 325 | case 1: 326 | src = argv[0]; 327 | break; 328 | default: 329 | rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc); 330 | } 331 | 332 | VALUE io = Qnil; 333 | if(rb_type(src) != T_STRING) { 334 | io = src; 335 | src = Qnil; 336 | } 337 | 338 | VALUE self = Unpacker_alloc(cMessagePack_Unpacker); 339 | UNPACKER(self, uk); 340 | //msgpack_unpacker_reset(s_unpacker); 341 | //msgpack_buffer_reset_io(UNPACKER_BUFFER_(s_unpacker)); 342 | 343 | /* prefer reference than copying */ 344 | msgpack_buffer_set_write_reference_threshold(UNPACKER_BUFFER_(uk), 0); 345 | 346 | uk->keys_as_symbols = keys_as_symbols; 347 | 348 | if(io != Qnil) { 349 | MessagePack_Buffer_initialize(UNPACKER_BUFFER_(uk), io, Qnil); 350 | } 351 | 352 | if(src != Qnil) { 353 | /* prefer reference than copying; see MessagePack_Unpacker_module_init */ 354 | msgpack_buffer_append_string(UNPACKER_BUFFER_(uk), src); 355 | } 356 | 357 | int r = msgpack_unpacker_read(uk, 0); 358 | if(r < 0) { 359 | raise_unpacker_error(r); 360 | } 361 | 362 | /* raise if extra bytes follow */ 363 | if(msgpack_buffer_top_readable_size(UNPACKER_BUFFER_(uk)) > 0) { 364 | rb_raise(eMalformedFormatError, "extra bytes follow after a deserialized object"); 365 | } 366 | 367 | #ifdef RB_GC_GUARD 368 | /* This prevents compilers from optimizing out the `self` variable 369 | * from stack. Otherwise GC free()s it. */ 370 | RB_GC_GUARD(self); 371 | #endif 372 | 373 | return msgpack_unpacker_get_last_object(uk); 374 | } 375 | 376 | static VALUE MessagePack_load_module_method(int argc, VALUE* argv, VALUE mod) 377 | { 378 | UNUSED(mod); 379 | return MessagePack_unpack(argc, argv); 380 | } 381 | 382 | static VALUE MessagePack_unpack_module_method(int argc, VALUE* argv, VALUE mod) 383 | { 384 | UNUSED(mod); 385 | return MessagePack_unpack(argc, argv); 386 | } 387 | 388 | void MessagePack_Unpacker_module_init(VALUE mMessagePack) 389 | { 390 | msgpack_unpacker_static_init(); 391 | 392 | cMessagePack_Unpacker = rb_define_class_under(mMessagePack, "Unpacker", rb_cObject); 393 | 394 | eUnpackError = rb_define_class_under(mMessagePack, "UnpackError", rb_eStandardError); 395 | 396 | eMalformedFormatError = rb_define_class_under(mMessagePack, "MalformedFormatError", eUnpackError); 397 | 398 | eStackError = rb_define_class_under(mMessagePack, "StackError", eUnpackError); 399 | 400 | eTypeError = rb_define_class_under(mMessagePack, "TypeError", rb_eStandardError); 401 | 402 | rb_define_alloc_func(cMessagePack_Unpacker, Unpacker_alloc); 403 | 404 | rb_define_method(cMessagePack_Unpacker, "initialize", Unpacker_initialize, -1); 405 | rb_define_method(cMessagePack_Unpacker, "buffer", Unpacker_buffer, 0); 406 | rb_define_method(cMessagePack_Unpacker, "read", Unpacker_read, 0); 407 | rb_define_alias(cMessagePack_Unpacker, "unpack", "read"); 408 | rb_define_method(cMessagePack_Unpacker, "skip", Unpacker_skip, 0); 409 | rb_define_method(cMessagePack_Unpacker, "skip_nil", Unpacker_skip_nil, 0); 410 | rb_define_method(cMessagePack_Unpacker, "read_array_header", Unpacker_read_array_header, 0); 411 | rb_define_method(cMessagePack_Unpacker, "read_map_header", Unpacker_read_map_header, 0); 412 | //rb_define_method(cMessagePack_Unpacker, "peek_next_type", Unpacker_peek_next_type, 0); // TODO 413 | rb_define_method(cMessagePack_Unpacker, "feed", Unpacker_feed, 1); 414 | rb_define_method(cMessagePack_Unpacker, "each", Unpacker_each, 0); 415 | rb_define_method(cMessagePack_Unpacker, "feed_each", Unpacker_feed_each, 1); 416 | rb_define_method(cMessagePack_Unpacker, "reset", Unpacker_reset, 0); 417 | 418 | //s_unpacker_value = Unpacker_alloc(cMessagePack_Unpacker); 419 | //rb_gc_register_address(&s_unpacker_value); 420 | //Data_Get_Struct(s_unpacker_value, msgpack_unpacker_t, s_unpacker); 421 | /* prefer reference than copying */ 422 | //msgpack_buffer_set_write_reference_threshold(UNPACKER_BUFFER_(s_unpacker), 0); 423 | 424 | /* MessagePack.unpack(x) */ 425 | rb_define_module_function(mMessagePack, "load", MessagePack_load_module_method, -1); 426 | rb_define_module_function(mMessagePack, "unpack", MessagePack_unpack_module_method, -1); 427 | rb_define_module_function(mMessagePack, "decode", MessagePack_unpack_module_method, -1); 428 | } 429 | 430 | -------------------------------------------------------------------------------- /ext/cbor/unpacker_class.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CBOR for Ruby 3 | * 4 | * Copyright (C) 2013 Carsten Bormann 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"). 7 | * 8 | * Based on: 9 | ***********/ 10 | /* 11 | * MessagePack for Ruby 12 | * 13 | * Copyright (C) 2008-2013 Sadayuki Furuhashi 14 | * 15 | * Licensed under the Apache License, Version 2.0 (the "License"); 16 | * you may not use this file except in compliance with the License. 17 | * You may obtain a copy of the License at 18 | * 19 | * http://www.apache.org/licenses/LICENSE-2.0 20 | * 21 | * Unless required by applicable law or agreed to in writing, software 22 | * distributed under the License is distributed on an "AS IS" BASIS, 23 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | * See the License for the specific language governing permissions and 25 | * limitations under the License. 26 | */ 27 | #ifndef MSGPACK_RUBY_UNPACKER_CLASS_H__ 28 | #define MSGPACK_RUBY_UNPACKER_CLASS_H__ 29 | 30 | #include "unpacker.h" 31 | 32 | extern VALUE cMessagePack_Unpacker; 33 | 34 | void MessagePack_Unpacker_module_init(VALUE mMessagePack); 35 | 36 | VALUE MessagePack_unpack(int argc, VALUE* argv); 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /lib/cbor.rb: -------------------------------------------------------------------------------- 1 | require "cbor/version" 2 | begin 3 | require "cbor/#{RUBY_VERSION[/\d+.\d+/]}/cbor" 4 | rescue LoadError 5 | require "cbor/cbor" 6 | end 7 | -------------------------------------------------------------------------------- /lib/cbor/version.rb: -------------------------------------------------------------------------------- 1 | module CBOR 2 | VERSION = "0.5.9.8" 3 | end 4 | -------------------------------------------------------------------------------- /spec/buffer_io_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'random_compat' 3 | 4 | require 'stringio' 5 | if defined?(Encoding) 6 | Encoding.default_external = 'ASCII-8BIT' 7 | end 8 | 9 | describe Buffer do 10 | r = Random.new 11 | random_seed = r.seed 12 | puts "buffer_io random seed: 0x#{random_seed.to_s(16)}" 13 | 14 | let :source do 15 | '' 16 | end 17 | 18 | def set_source(s) 19 | source.replace(s) 20 | end 21 | 22 | let :io do 23 | StringIO.new(source.dup) 24 | end 25 | 26 | let :buffer do 27 | Buffer.new(io) 28 | end 29 | 30 | let :repetition do 31 | ENV['SLOW'] ? 3 : 50 32 | end 33 | 34 | it 'io returns internal io' do 35 | buffer.io.should == io 36 | end 37 | 38 | it 'close closes internal io' do 39 | io.should_receive(:close) 40 | buffer.close 41 | end 42 | 43 | it 'short feed and read all' do 44 | set_source 'aa' 45 | buffer.read.should == 'aa' 46 | end 47 | 48 | it 'short feed and read short' do 49 | set_source 'aa' 50 | buffer.read(1).should == 'a' 51 | buffer.read(1).should == 'a' 52 | buffer.read(1).should == nil 53 | end 54 | 55 | it 'long feed and read all' do 56 | set_source ' '*(1024*1024) 57 | s = buffer.read 58 | s.size.should == source.size 59 | s.should == source 60 | end 61 | 62 | it 'long feed and read mixed' do 63 | set_source ' '*(1024*1024) 64 | buffer.read(10).should == source.slice!(0, 10) 65 | buffer.read(10).should == source.slice!(0, 10) 66 | buffer.read(10).should == source.slice!(0, 10) 67 | s = buffer.read 68 | s.size.should == source.size 69 | s.should == source 70 | end 71 | 72 | it 'eof' do 73 | set_source '' 74 | buffer.read.should == '' 75 | end 76 | 77 | it 'eof 2' do 78 | set_source 'a' 79 | buffer.read.should == 'a' 80 | buffer.read.should == '' 81 | end 82 | 83 | it 'write short once and flush' do 84 | buffer.write('aa') 85 | buffer.flush 86 | io.string.should == 'aa' 87 | end 88 | 89 | it 'write short twice and flush' do 90 | buffer.write('a') 91 | buffer.write('a') 92 | buffer.flush 93 | io.string.should == 'aa' 94 | end 95 | 96 | it 'write long once and flush' do 97 | s = ' '*(1024*1024) 98 | buffer.write s 99 | buffer.flush 100 | io.string.size.should == s.size 101 | io.string.should == s 102 | end 103 | 104 | it 'write short multi and flush' do 105 | s = ' '*(1024*1024) 106 | 1024.times { 107 | buffer.write ' '*1024 108 | } 109 | buffer.flush 110 | io.string.size.should == s.size 111 | io.string.should == s 112 | end 113 | 114 | it 'random read' do 115 | r = Random.new(random_seed) 116 | 117 | repetition.times { 118 | fragments = [] 119 | 120 | r.rand(4).times do 121 | n = r.rand(1024*1400) 122 | s = r.bytes(n) 123 | fragments << s 124 | end 125 | 126 | io = StringIO.new(fragments.join) 127 | b = Buffer.new(io) 128 | 129 | fragments.each {|s| 130 | x = b.read(s.size) 131 | x.size.should == s.size 132 | x.should == s 133 | } 134 | b.empty?.should == true 135 | b.read.should == '' 136 | } 137 | end 138 | 139 | it 'random read_all' do 140 | r = Random.new(random_seed) 141 | 142 | repetition.times { 143 | fragments = [] 144 | sx = r.bytes(0) 145 | 146 | r.rand(4).times do 147 | n = r.rand(1024*1400) 148 | s = r.bytes(n) 149 | fragments << s 150 | end 151 | 152 | io = StringIO.new(fragments.join) 153 | b = Buffer.new(io) 154 | 155 | fragments.each {|s| 156 | x = b.read_all(s.size) 157 | x.size.should == s.size 158 | x.should == s 159 | } 160 | b.empty?.should == true 161 | lambda { 162 | b.read_all(1) 163 | }.should raise_error(EOFError) 164 | } 165 | end 166 | 167 | it 'random skip' do 168 | r = Random.new(random_seed) 169 | 170 | repetition.times { 171 | fragments = [] 172 | 173 | r.rand(4).times do 174 | n = r.rand(1024*1400) 175 | s = r.bytes(n) 176 | fragments << s 177 | end 178 | 179 | io = StringIO.new(fragments.join) 180 | b = Buffer.new(io) 181 | 182 | fragments.each {|s| 183 | b.skip(s.size).should == s.size 184 | } 185 | b.empty?.should == true 186 | b.skip(1).should == 0 187 | } 188 | end 189 | 190 | it 'random skip_all' do 191 | r = Random.new(random_seed) 192 | 193 | repetition.times { 194 | fragments = [] 195 | 196 | r.rand(4).times do 197 | n = r.rand(1024*1400) 198 | s = r.bytes(n) 199 | fragments << s 200 | end 201 | 202 | io = StringIO.new(fragments.join) 203 | b = Buffer.new(io) 204 | 205 | fragments.each {|s| 206 | lambda { 207 | b.skip_all(s.size) 208 | }.should_not raise_error() 209 | } 210 | b.empty?.should == true 211 | lambda { 212 | b.skip_all(1) 213 | }.should raise_error(EOFError) 214 | } 215 | end 216 | 217 | it 'random write and flush' do 218 | r = Random.new(random_seed) 219 | 220 | repetition.times { 221 | s = r.bytes(0) 222 | io = StringIO.new 223 | b = Buffer.new(io) 224 | 225 | r.rand(4).times do 226 | n = r.rand(1024*1400) 227 | x = r.bytes(n) 228 | s << x 229 | b.write(x) 230 | end 231 | 232 | (io.string.size + b.size).should == s.size 233 | 234 | b.flush 235 | 236 | io.string.size.should == s.size 237 | io.string.should == s 238 | } 239 | end 240 | 241 | it 'random write and clear' do 242 | r = Random.new(random_seed) 243 | b = Buffer.new 244 | 245 | repetition.times { 246 | s = r.bytes(0) 247 | 248 | r.rand(4).times do 249 | n = r.rand(1024*1400) 250 | x = r.bytes(n) 251 | s << x 252 | b.write(x) 253 | end 254 | 255 | b.size.should == s.size 256 | b.clear 257 | } 258 | end 259 | end 260 | 261 | -------------------------------------------------------------------------------- /spec/buffer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'random_compat' 3 | 4 | describe Buffer do 5 | STATIC_EXAMPLES = {} 6 | STATIC_EXAMPLES[:empty01] = '' 7 | STATIC_EXAMPLES[:empty02] = '' 8 | STATIC_EXAMPLES[:copy01] = 'short' 9 | STATIC_EXAMPLES[:copy02] = 'short'*2 10 | STATIC_EXAMPLES[:ref01] = 'short'*128 11 | STATIC_EXAMPLES[:ref02] = 'short'*128*2 12 | STATIC_EXAMPLES[:ref03] = 'a'*((1024*1024+2)*2) 13 | STATIC_EXAMPLES[:refcopy01] = 'short'*128 14 | STATIC_EXAMPLES[:expand01] = 'short'*1024 15 | STATIC_EXAMPLES[:expand02] = 'short'*(127+1024) 16 | STATIC_EXAMPLES[:offset01] = 'ort'+'short' 17 | STATIC_EXAMPLES[:offset02] = 'ort'+'short'*127 18 | STATIC_EXAMPLES[:offset03] = 'ort'+'short'*(126+1024) 19 | 20 | if ''.respond_to?(:force_encoding) 21 | STATIC_EXAMPLES.each_value {|v| v.force_encoding('ASCII-8BIT') } 22 | end 23 | STATIC_EXAMPLES.each_value {|v| v.freeze } 24 | 25 | r = Random.new 26 | random_seed = r.seed 27 | puts "buffer random seed: 0x#{random_seed.to_s(16)}" 28 | 29 | let :repetition do 30 | ENV['SLOW'] ? 2 : 10 31 | end 32 | 33 | let :random_cases_examples do 34 | r = Random.new(random_seed) 35 | cases = {} 36 | examples = {} 37 | 38 | repetition.times do |i| 39 | b = Buffer.new 40 | s = r.bytes(0) 41 | r.rand(3).times do 42 | n = r.rand(1024*1400) 43 | x = r.bytes(n) 44 | s << x 45 | b << x 46 | end 47 | r.rand(2).times do 48 | n = r.rand(1024*1400) 49 | b.read(n) 50 | s.slice!(0, n) 51 | end 52 | key = :"random#{"%02d"%i}" 53 | cases[key] = b 54 | examples[key] = s 55 | end 56 | 57 | [cases, examples] 58 | end 59 | 60 | let :static_cases do 61 | map = {} 62 | map[:empty01] = empty01 63 | map[:empty02] = empty02 64 | map[:copy01] = copy01 65 | map[:copy02] = copy02 66 | map[:ref01] = ref01 67 | map[:ref02] = ref02 68 | map[:ref03] = ref03 69 | map[:refcopy01] = refcopy01 70 | map[:expand01] = expand01 71 | map[:expand02] = expand02 72 | map[:offset01] = offset01 73 | map[:offset02] = offset02 74 | map[:offset03] = offset03 75 | map 76 | end 77 | 78 | let :static_examples do 79 | STATIC_EXAMPLES 80 | end 81 | 82 | let :random_cases do 83 | random_cases_examples[0] 84 | end 85 | 86 | let :random_examples do 87 | random_cases_examples[1] 88 | end 89 | 90 | let :cases do 91 | static_cases.merge(random_cases) 92 | end 93 | 94 | let :examples do 95 | static_examples.merge(random_examples) 96 | end 97 | 98 | let :case_keys do 99 | examples.keys 100 | end 101 | 102 | let :empty01 do 103 | Buffer.new 104 | end 105 | 106 | let :empty02 do 107 | b = Buffer.new 108 | b << 'a' 109 | b.read_all(1) 110 | b 111 | end 112 | 113 | let :copy01 do 114 | # one copy chunk 115 | b = Buffer.new 116 | b << 'short' 117 | b 118 | end 119 | 120 | let :copy02 do 121 | # one copy chunk 122 | b = Buffer.new 123 | b << 'short' 124 | b << 'short' 125 | b 126 | end 127 | 128 | let :expand02 do 129 | # one copy chunk / expanded 130 | b = Buffer.new 131 | 1024.times do 132 | b << 'short' 133 | end 134 | b 135 | end 136 | 137 | let :ref01 do 138 | # one reference chunk 139 | b = Buffer.new 140 | b << 'short'*128 141 | b.to_s 142 | b 143 | end 144 | 145 | let :ref02 do 146 | # two reference chunks 147 | b = Buffer.new 148 | b << 'short'*128 149 | b.to_s 150 | b << 'short'*128 151 | b.to_a 152 | b 153 | end 154 | 155 | let :ref03 do 156 | # two reference chunks 157 | b = Buffer.new 158 | b << 'a'*(1024*1024+2) 159 | b << 'a'*(1024*1024+2) 160 | b 161 | end 162 | 163 | let :refcopy01 do 164 | # one reference chunk + one copy chunk 165 | b = Buffer.new 166 | b << 'short'*127 167 | b.to_s 168 | b << 'short' 169 | b 170 | end 171 | 172 | let :expand01 do 173 | # one copy chunk / expanded 174 | b = Buffer.new 175 | 1024.times do 176 | b << 'short' 177 | end 178 | b 179 | end 180 | 181 | let :expand02 do 182 | # one reference chunk + one copy chunk / expanded 183 | b = Buffer.new 184 | b << 'short'*127 185 | b.to_s 186 | 1024.times do 187 | b << 'short' 188 | end 189 | b 190 | end 191 | 192 | let :offset01 do 193 | # one copy chunk / offset 194 | b = Buffer.new 195 | b << 'short' 196 | b << 'short' 197 | b.skip(2) 198 | b 199 | end 200 | 201 | let :offset02 do 202 | # one reference chunk / offset 203 | b = Buffer.new 204 | b << 'short'*127 205 | b.to_s 206 | b.skip(2) 207 | b << 'short' 208 | b 209 | end 210 | 211 | let :offset03 do 212 | # one reference chunk / offset + one copy chunk / expanded 213 | b = Buffer.new 214 | b << 'short'*127 215 | b.to_s 216 | 1024.times do 217 | b << 'short' 218 | end 219 | b.skip(2) 220 | b 221 | end 222 | 223 | it 'empty?' do 224 | empty01.empty?.should == true 225 | empty02.empty?.should == true 226 | end 227 | 228 | it 'size' do 229 | case_keys.each {|k| 230 | cases[k].size.should == examples[k].size 231 | } 232 | end 233 | 234 | it 'short write increments size' do 235 | case_keys.each {|k| 236 | sz = examples[k].size 237 | 10.times do |i| 238 | cases[k].write 'short' 239 | sz += 'short'.size 240 | cases[k].size.should == sz 241 | end 242 | } 243 | end 244 | 245 | it 'read with limit decrements size' do 246 | case_keys.each {|k| 247 | sz = examples[k].size 248 | next if sz < 2 249 | 250 | cases[k].read(1).should == examples[k][0,1] 251 | sz -= 1 252 | cases[k].size.should == sz 253 | 254 | cases[k].read(1).should == examples[k][1,1] 255 | sz -= 1 256 | cases[k].size.should == sz 257 | } 258 | end 259 | 260 | it 'read_all with limit decrements size' do 261 | case_keys.each {|k| 262 | sz = examples[k].size 263 | next if sz < 2 264 | 265 | cases[k].read_all(1).should == examples[k][0,1] 266 | sz -= 1 267 | cases[k].size.should == sz 268 | 269 | cases[k].read_all(1).should == examples[k][1,1] 270 | sz -= 1 271 | cases[k].size.should == sz 272 | } 273 | end 274 | 275 | it 'skip decrements size' do 276 | case_keys.each {|k| 277 | sz = examples[k].size 278 | next if sz < 2 279 | 280 | cases[k].skip(1).should == 1 281 | sz -= 1 282 | cases[k].size.should == sz 283 | 284 | cases[k].skip(1).should == 1 285 | sz -= 1 286 | cases[k].size.should == sz 287 | } 288 | end 289 | 290 | it 'skip_all decrements size' do 291 | case_keys.each {|k| 292 | sz = examples[k].size 293 | next if sz < 2 294 | 295 | cases[k].skip_all(1) 296 | sz -= 1 297 | cases[k].size.should == sz 298 | 299 | cases[k].skip_all(1) 300 | sz -= 1 301 | cases[k].size.should == sz 302 | } 303 | end 304 | 305 | it 'read_all against insufficient buffer raises EOFError and consumes nothing' do 306 | case_keys.each {|k| 307 | sz = examples[k].size 308 | lambda { 309 | cases[k].read_all(sz+1) 310 | }.should raise_error(EOFError) 311 | cases[k].size.should == sz 312 | } 313 | end 314 | 315 | it 'skip_all against insufficient buffer raises EOFError and consumes nothing' do 316 | case_keys.each {|k| 317 | sz = examples[k].size 318 | lambda { 319 | cases[k].skip_all(sz+1) 320 | }.should raise_error(EOFError) 321 | cases[k].size.should == sz 322 | } 323 | end 324 | 325 | it 'read against insufficient buffer consumes all buffer or return nil' do 326 | case_keys.each {|k| 327 | sz = examples[k].size 328 | if sz == 0 329 | cases[k].read(sz+1).should == nil 330 | else 331 | cases[k].read(sz+1).should == examples[k] 332 | end 333 | cases[k].size.should == 0 334 | } 335 | end 336 | 337 | it 'skip against insufficient buffer consumes all buffer' do 338 | case_keys.each {|k| 339 | sz = examples[k].size 340 | cases[k].skip(sz+1).should == examples[k].size 341 | cases[k].size.should == 0 342 | } 343 | end 344 | 345 | it 'read with no arguments consumes all buffer and returns string and do not return nil' do 346 | case_keys.each {|k| 347 | cases[k].read_all.should == examples[k] 348 | cases[k].size.should == 0 349 | } 350 | end 351 | 352 | it 'read_all with no arguments consumes all buffer and returns string' do 353 | case_keys.each {|k| 354 | cases[k].read_all.should == examples[k] 355 | cases[k].size.should == 0 356 | } 357 | end 358 | 359 | it 'to_s returns a string and consume nothing' do 360 | case_keys.each {|k| 361 | cases[k].to_s.should == examples[k] 362 | cases[k].size.should == examples[k].size 363 | } 364 | end 365 | 366 | it 'to_s and modify' do 367 | case_keys.each {|k| 368 | s = cases[k].to_s 369 | s << 'x' 370 | cases[k].to_s.should == examples[k] 371 | } 372 | end 373 | 374 | it 'big write and modify reference' do 375 | big2 = "a" * (1024*1024 + 2) 376 | 377 | case_keys.each {|k| 378 | big1 = "a" * (1024*1024 + 2) 379 | cases[k].write(big1) 380 | big1 << 'x' 381 | cases[k].read.should == examples[k] + big2 382 | } 383 | end 384 | 385 | it 'big write -> short write' do 386 | biglen = 1024*1024 + 2 387 | big1 = "a" * (1024*1024 + 2) 388 | 389 | case_keys.each {|k| 390 | sz = examples[k].size 391 | 392 | cases[k].write big1 393 | cases[k].size.should == sz + big1.size 394 | 395 | cases[k].write("c") 396 | cases[k].size.should == sz + big1.size + 1 397 | 398 | cases[k].read_all.should == examples[k] + big1 + "c" 399 | cases[k].size.should == 0 400 | cases[k].empty?.should == true 401 | } 402 | end 403 | 404 | it 'big write 2'do 405 | big1 = "a" * (1024*1024 + 2) 406 | big2 = "b" * (1024*1024 + 2) 407 | 408 | case_keys.each {|k| 409 | sz = examples[k].size 410 | 411 | cases[k].write big1 412 | cases[k].write big2 413 | cases[k].size.should == sz + big1.size + big2.size 414 | 415 | cases[k].read_all(sz + big1.size).should == examples[k] + big1 416 | cases[k].size.should == big2.size 417 | 418 | cases[k].read.should == big2 419 | cases[k].size.should == 0 420 | cases[k].empty?.should == true 421 | } 422 | end 423 | 424 | it 'read 0' do 425 | case_keys.each {|k| 426 | cases[k].read(0).should == '' 427 | cases[k].read.should == examples[k] 428 | } 429 | end 430 | 431 | it 'skip 0' do 432 | case_keys.each {|k| 433 | cases[k].skip(0).should == 0 434 | cases[k].read.should == examples[k] 435 | } 436 | end 437 | 438 | it 'read_all 0' do 439 | case_keys.each {|k| 440 | cases[k].read_all(0).should == '' 441 | cases[k].read_all.should == examples[k] 442 | } 443 | end 444 | 445 | it 'skip_all 0' do 446 | case_keys.each {|k| 447 | cases[k].skip_all(0) 448 | cases[k].read_all.should == examples[k] 449 | } 450 | end 451 | 452 | it 'write_to' do 453 | case_keys.each {|k| 454 | sio = StringIO.new 455 | cases[k].write_to(sio).should == examples[k].size 456 | cases[k].size.should == 0 457 | sio.string.should == examples[k] 458 | } 459 | end 460 | 461 | it 'random read/write' do 462 | r = Random.new(random_seed) 463 | s = r.bytes(0) 464 | b = Buffer.new 465 | 466 | 10.times { 467 | # write 468 | r.rand(4).times do 469 | n = r.rand(1024*1400) 470 | x = r.bytes(n) 471 | s << x 472 | b.write(x) 473 | end 474 | 475 | # read 476 | r.rand(3).times do 477 | n = r.rand(1024*1400) 478 | ex = s.slice!(0, n) 479 | ex = nil if ex.empty? 480 | x = b.read(n) 481 | x.size == ex.size if x != nil 482 | x.should == ex 483 | b.size.should == s.size 484 | end 485 | } 486 | end 487 | 488 | it 'random read_all/write' do 489 | r = Random.new(random_seed) 490 | s = r.bytes(0) 491 | b = Buffer.new 492 | 493 | 10.times { 494 | # write 495 | r.rand(4).times do 496 | n = r.rand(1024*1400) 497 | x = r.bytes(n) 498 | s << x 499 | b.write(x) 500 | end 501 | 502 | # read_all 503 | r.rand(3).times do 504 | n = r.rand(1024*1400) 505 | begin 506 | x = b.read_all(n) 507 | ex = s.slice!(0, n) 508 | x.size == n 509 | x.should == ex 510 | b.size.should == s.size 511 | rescue EOFError 512 | b.size.should == s.size 513 | b.read.should == s 514 | s.clear 515 | break 516 | end 517 | end 518 | } 519 | end 520 | 521 | it 'random skip write' do 522 | r = Random.new(random_seed) 523 | s = r.bytes(0) 524 | b = Buffer.new 525 | 526 | 10.times { 527 | # write 528 | r.rand(4).times do 529 | n = r.rand(1024*1400) 530 | x = r.bytes(n) 531 | s << x 532 | b.write(x) 533 | end 534 | 535 | # skip 536 | r.rand(3).times do 537 | n = r.rand(1024*1400) 538 | ex = s.slice!(0, n) 539 | b.skip(n).should == ex.size 540 | b.size.should == s.size 541 | end 542 | } 543 | end 544 | 545 | it 'random skip_all write' do 546 | r = Random.new(random_seed) 547 | s = r.bytes(0) 548 | b = Buffer.new 549 | 550 | 10.times { 551 | # write 552 | r.rand(4).times do 553 | n = r.rand(1024*1400) 554 | x = r.bytes(n) 555 | s << x 556 | b.write(x) 557 | end 558 | 559 | # skip_all 560 | r.rand(3).times do 561 | n = r.rand(1024*1400) 562 | begin 563 | b.skip_all(n) 564 | ex = s.slice!(0, n) 565 | b.size.should == s.size 566 | ensure EOFError 567 | b.size.should == s.size 568 | b.read.should == s 569 | s.clear 570 | break 571 | end 572 | end 573 | } 574 | end 575 | end 576 | 577 | -------------------------------------------------------------------------------- /spec/cases.cbor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabo/cbor-ruby/4e8b2ec982f41cfcd6568a429add205c8259f470/spec/cases.cbor -------------------------------------------------------------------------------- /spec/cases.cbor_stream: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabo/cbor-ruby/4e8b2ec982f41cfcd6568a429add205c8259f470/spec/cases.cbor_stream -------------------------------------------------------------------------------- /spec/cases.json: -------------------------------------------------------------------------------- 1 | [false,true,null,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,127,127,255,65535,4294967295,-32,-32,-128,-32768,-2147483648,0.0,-0.0,1.0,-1.0,"a","a","a","","","",[0],[0],[0],[],[],[],{},{},{},{"a":97},{"a":97},{"a":97},[[]],[["a"]]] -------------------------------------------------------------------------------- /spec/cases.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabo/cbor-ruby/4e8b2ec982f41cfcd6568a429add205c8259f470/spec/cases.msg -------------------------------------------------------------------------------- /spec/cases_compact.msg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cabo/cbor-ruby/4e8b2ec982f41cfcd6568a429add205c8259f470/spec/cases_compact.msg -------------------------------------------------------------------------------- /spec/cases_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'json' 3 | 4 | describe MessagePack do 5 | here = File.dirname(__FILE__) 6 | CASES = File.read("#{here}/cases.cbor_stream") 7 | CASES_JSON = File.read("#{here}/cases.json") 8 | CASES_INDEFINITE = File.read("#{here}/cases.cbor_stream") # TODO 9 | 10 | it 'compare with json' do 11 | ms = [] 12 | MessagePack::Unpacker.new.feed_each(CASES) {|m| 13 | ms << m 14 | } 15 | 16 | js = JSON.load(CASES_JSON) 17 | 18 | ms.zip(js) {|m,j| 19 | m.should == j 20 | } 21 | end 22 | 23 | it 'compare with compat' do 24 | ms = [] 25 | MessagePack::Unpacker.new.feed_each(CASES) {|m| 26 | ms << m 27 | } 28 | 29 | cs = [] 30 | MessagePack::Unpacker.new.feed_each(CASES_INDEFINITE) {|c| 31 | cs << c 32 | } 33 | 34 | ms.zip(cs) {|m,c| 35 | m.should == c 36 | } 37 | end 38 | end 39 | 40 | -------------------------------------------------------------------------------- /spec/format_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | def bignum_to_bytes(bn) 5 | bn.to_s(16).chars.each_slice(2).map{|a| a.join.to_i(16).chr}.join 6 | end 7 | # => "\x98!sHq#\x98W\x91\x82sY\x87)Hu#E" 8 | # 0x982173487123985791827359872948752345 9 | 10 | unless defined?(Float::INFINITY) 11 | class Float 12 | INFINITY = 1.0/0.0 13 | NAN = 0.0/0.0 14 | end 15 | end 16 | 17 | describe MessagePack do 18 | it "nil" do 19 | check 1, nil 20 | end 21 | 22 | it "true" do 23 | check 1, true 24 | end 25 | 26 | it "false" do 27 | check 1, false 28 | end 29 | 30 | it "zero" do 31 | check 1, 0 32 | end 33 | 34 | it "positive fixnum" do 35 | check 1, 1 36 | check 2, (1<<6) 37 | check 2, (1<<7)-1 38 | end 39 | 40 | it "positive int 8" do 41 | check 1, -1 42 | check 2, (1<<7) 43 | check 2, (1<<8)-1 44 | end 45 | 46 | it "positive int 16" do 47 | check 3, (1<<8) 48 | check 3, (1<<16)-1 49 | check 3, 1024 50 | check 4, [1024] 51 | end 52 | 53 | it "positive int 32" do 54 | check 5, (1<<16) 55 | check 5, (1<<32)-1 56 | end 57 | 58 | it "positive int 64" do 59 | check 9, (1<<32) 60 | check 9, (1<<64)-1 61 | end 62 | 63 | it "negative fixnum" do 64 | check 1, -1 65 | check 1, -24 66 | check 2, -25 67 | end 68 | 69 | it "negative int 8" do 70 | check 2, -((1<<5)+1) 71 | check 2, -(1<<7) 72 | end 73 | 74 | it "negative int 16" do 75 | check 2, -((1<<7)+1) 76 | check 2, -256 77 | check 3, -(1<<15) 78 | end 79 | 80 | it "negative int 32" do 81 | check 3, -((1<<15)+1) 82 | check 3, -(1<<16) 83 | check 5, -(1<<31) 84 | check 5, -(1<<32) 85 | end 86 | 87 | it "negative int 64" do 88 | check 5, -((1<<31)+1) 89 | check 9, -(1<<63) 90 | check 9, -(1<<64) 91 | end 92 | 93 | it "half" do 94 | check 3, 1.0 95 | check 3, -1.0 96 | check 3, -2.0 97 | check 3, 65504.0 98 | check 3, -65504.0 99 | check 3, Math.ldexp(1, -14) # ≈ 6.10352 × 10−5 (minimum positive normal) 100 | check 3, -Math.ldexp(1, -14) # ≈ 6.10352 × 10−5 (maximum negative normal) 101 | check 3, Math.ldexp(1, -14) - Math.ldexp(1, -24) # ≈ 6.09756 × 10−5 (maximum subnormal) 102 | check 3, Math.ldexp(1, -24) # ≈ 5.96046 × 10−8 (minimum positive subnormal) 103 | check 3, -Math.ldexp(1, -24) # ≈ -5.96046 × 10−8 (maximum negative subnormal) 104 | check 5, Math.ldexp(1, -14) - Math.ldexp(0.5, -24) # check loss of precision 105 | check 5, Math.ldexp(1.5, -24) # loss of precision (subnormal) 106 | check 3, Math.ldexp(1.5, -23) 107 | check 5, Math.ldexp(1.75, -23) 108 | check 3, Float::INFINITY 109 | check 3, -Float::INFINITY 110 | # check 3, Float::NAN # NAN is not equal to itself, to this never checks out... 111 | raw = Float::NAN.to_cbor.to_s 112 | raw.length.should == 3 113 | MessagePack.unpack(raw).nan?.should == true 114 | end 115 | 116 | it "double" do 117 | check 9, 0.1 118 | check 9, -0.1 119 | end 120 | 121 | it "fixraw" do 122 | check_raw 1, 0 123 | check_raw 1, 23 124 | end 125 | 126 | it "raw 16" do 127 | check_raw 2, (1<<5) 128 | check_raw 3, (1<<16)-1 129 | end 130 | 131 | it "raw 32" do 132 | check_raw 5, (1<<16) 133 | #check_raw 5, (1<<32)-1 # memory error 134 | end 135 | 136 | it "fixarray" do 137 | check_array 1, 0 138 | check_array 1, (1<<4)-1 139 | check_array 1, 23 140 | end 141 | 142 | it "array 16" do 143 | check_array 1, (1<<4) 144 | #check_array 3, (1<<16)-1 145 | end 146 | 147 | it "array 32" do 148 | #check_array 5, (1<<16) 149 | #check_array 5, (1<<32)-1 # memory error 150 | end 151 | 152 | it "nil" do 153 | match nil, "\xf6".b 154 | end 155 | 156 | it "false" do 157 | match false, "\xf4".b 158 | end 159 | 160 | it "true" do 161 | match true, "\xf5".b 162 | end 163 | 164 | it "0" do 165 | match 0, "\x00".b 166 | end 167 | 168 | it "127" do 169 | match 127, "\x18\x7f".b 170 | end 171 | 172 | it "128" do 173 | match 128, "\x18\x80".b 174 | end 175 | 176 | it "256" do 177 | match 256, "\x19\x01\x00".b 178 | end 179 | 180 | it "-1" do 181 | match -1, "\x20".b 182 | end 183 | 184 | it "-33" do 185 | match -33, "\x38\x20".b 186 | end 187 | 188 | it "-129" do 189 | match -129, "\x38\x80".b 190 | end 191 | 192 | it "-257" do 193 | match -257, "\x39\x01\x00".b 194 | end 195 | 196 | it "{1=>1}" do 197 | obj = {1=>1} 198 | match obj, "\xA1\x01\x01".b 199 | end 200 | 201 | it "1.0" do 202 | match 1.0, "\xF9\x3c\x00".b 203 | end 204 | 205 | it "NaN" do 206 | match Float::NAN, "\xF9\x7e\x00".b 207 | end 208 | 209 | it "[]" do 210 | match [], "\x80".b 211 | end 212 | 213 | it "[0, 1, ..., 14]" do 214 | obj = (0..14).to_a 215 | match obj, "\x8f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e".b 216 | end 217 | 218 | it "[0, 1, ..., 15]" do 219 | obj = (0..15).to_a 220 | match obj, "\x90\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f".b 221 | end 222 | 223 | it "[0, 1, ..., 22]" do 224 | obj = (0..22).to_a 225 | match obj, "\x97\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16".b 226 | end 227 | 228 | it "[0, 1, ..., 23]" do 229 | obj = (0..23).to_a 230 | match obj, "\x98\x18\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17".b 231 | end 232 | 233 | it "{}" do 234 | obj = {} 235 | match obj, "\xA0".b 236 | end 237 | 238 | it "very simple bignums" do 239 | CBOR.decode("\xc2\x40").should == 0 240 | CBOR.decode("\xc2\x41\x00").should == 0 241 | CBOR.decode("\xc2\x41a").should == 97 242 | CBOR.decode("\xc2\x42aa").should == 24929 243 | CBOR.decode("\xc3\x40").should == ~0 244 | CBOR.decode("\xc3\x41\x00").should == ~0 245 | CBOR.decode("\xc3\x41a").should == ~97 246 | CBOR.decode("\xc3\x42aa").should == ~24929 247 | end 248 | 249 | it "0x982173487123985791827359872948752345" do 250 | check_bn(0x982173487123985791827359872948752345, 0xc2) 251 | check_bn(-0x982173487123985791827359872948752345, 0xc3) 252 | check_bn(0x9821734871239857918273598729487523, 0xc2) 253 | check_bn(0x98217348712398579182735987294875, 0xc2) 254 | check_bn(0x982173487123985791827359872948, 0xc2) 255 | check_bn(0x9821734871239857918273598729, 0xc2) 256 | check_bn(0x98217348712398579182735987, 0xc2) 257 | check 20, 0x982173487123985791827359872948752345 258 | check 20, -0x982173487123985791827359872948752345 259 | check 19, 0x9821734871239857918273598729487523 260 | check 18, 0x98217348712398579182735987294875 261 | check 18, -0x98217348712398579182735987294875 262 | check 17, 0x982173487123985791827359872948 263 | check 16, 0x9821734871239857918273598729 264 | check 15, 0x98217348712398579182735987 265 | check 15, 0x9821734871239857918273598 266 | check 16, [0x9821734871239857918273598] 267 | check 39, 0x982173487123985791827359872948752345982173487123985791827359872948752345 268 | check 3005, 256**3000+4711 # 3001 bignum, 3 string, 1 tag 269 | end 270 | 271 | it "fixnum/bignum switch" do 272 | CBOR.encode(CBOR.decode("\xc2\x40")).should == "\x00".b 273 | CBOR.encode(CBOR.decode("\xc2\x41\x00")).should == "\x00".b 274 | CBOR.encode(CBOR.decode("\xc2\x41a")).should == "\x18a".b 275 | CBOR.encode(CBOR.decode("\xc2\x42aa")).should == "\x19aa".b 276 | CBOR.encode(CBOR.decode("\xc2\x43aaa")).should == "\x1A\x00aaa".b 277 | CBOR.encode(CBOR.decode("\xc2\x44aaaa")).should == "\x1Aaaaa".b 278 | CBOR.encode(CBOR.decode("\xc2\x45aaaaa")).should == "\e\x00\x00\x00aaaaa".b 279 | CBOR.encode(CBOR.decode("\xc2\x46aaaaaa")).should == "\e\x00\x00aaaaaa".b 280 | CBOR.encode(CBOR.decode("\xc2\x47aaaaaaa")).should == "\e\x00aaaaaaa".b 281 | CBOR.encode(CBOR.decode("\xc2\x48aaaaaaaa")).should == "\eaaaaaaaa".b 282 | CBOR.encode(CBOR.decode("\xc2\x49\x00aaaaaaaa")).should == "\eaaaaaaaa".b 283 | CBOR.encode(CBOR.decode("\xc2\x49aaaaaaaaa")).should == "\xC2Iaaaaaaaaa".b 284 | CBOR.encode(CBOR.decode("\xc2\x4a\x00aaaaaaaaa")).should == "\xC2Iaaaaaaaaa".b 285 | CBOR.encode(CBOR.decode("\xc2\x4aaaaaaaaaaa")).should == "\xC2Jaaaaaaaaaa".b 286 | CBOR.encode(CBOR.decode("\xc2\x4b\x00aaaaaaaaaa")).should == "\xC2Jaaaaaaaaaa".b 287 | CBOR.encode(CBOR.decode("\xc2\x4baaaaaaaaaaa")).should == "\xC2Kaaaaaaaaaaa".b 288 | CBOR.encode(CBOR.decode("\xc2\x4c\x00aaaaaaaaaaa")).should == "\xC2Kaaaaaaaaaaa".b 289 | CBOR.encode(CBOR.decode("\xc2\x4caaaaaaaaaaaa")).should == "\xC2Laaaaaaaaaaaa".b 290 | CBOR.encode(CBOR.decode("\xc2\x4d\x00aaaaaaaaaaaa")).should == "\xC2Laaaaaaaaaaaa".b 291 | CBOR.encode(CBOR.decode("\xc2\x4daaaaaaaaaaaaa")).should == "\xC2Maaaaaaaaaaaaa".b 292 | end 293 | 294 | it "a-non-ascii" do 295 | if ''.respond_to? :encode 296 | match "abc".encode("UTF-32BE"), "cabc" 297 | end 298 | end 299 | 300 | it "a" do 301 | match "a".encode_as_utf8, "aa" 302 | check 2, "a".encode_as_utf8 303 | check_decode "aa", "a".encode_as_utf8 304 | end 305 | 306 | it "a.b" do 307 | if ''.respond_to? :encode 308 | match "a".b, "Aa" 309 | end 310 | check 2, "a".b 311 | check_decode "Aa", "a".b 312 | end 313 | 314 | it "[_ ]" do 315 | check_decode "\x9f\xff", [] 316 | end 317 | 318 | it "[_ 1]" do 319 | check_decode "\x9f\x01\xff", [1] 320 | end 321 | 322 | it "{_ }" do 323 | check_decode "\xbf\xff", {} 324 | end 325 | 326 | it "{_ 1 => 2}" do 327 | check_decode "\xbf\x01\x02\xff", {1 => 2} 328 | end 329 | 330 | 331 | it "{_ 1 => BREAK}" do 332 | lambda { 333 | check_decode "\xbf\x01\xff", {1 => 2} 334 | }.should raise_error(MessagePack::MalformedFormatError) 335 | end 336 | 337 | it "(_ a b)" do 338 | check_decode "\x7f\xff", "".encode_as_utf8 339 | check_decode "\x5f\xff", "".b 340 | check_decode "\x7faa\xff", "a".encode_as_utf8 341 | check_decode "\x5fAa\xff", "a".b 342 | check_decode "\x7faabbb\xff", "abb".encode_as_utf8 343 | check_decode "\x5fAaBbb\xff", "abb".b 344 | if ''.respond_to? :encode 345 | lambda { 346 | check_decode "\x7faaBbb\xff", "abb".encode_as_utf8 347 | }.should raise_error(MessagePack::MalformedFormatError) 348 | lambda { 349 | check_decode "\x7fAa\xff", "a".encode_as_utf8 350 | }.should raise_error(MessagePack::MalformedFormatError) 351 | lambda { 352 | check_decode "\x5fAabbb\xff", "abb".b 353 | }.should raise_error(MessagePack::MalformedFormatError) 354 | lambda { 355 | check_decode "\x5faa\xff", "a".b 356 | }.should raise_error(MessagePack::MalformedFormatError) 357 | end 358 | end 359 | 360 | it "(_ not-a-string)" do 361 | lambda { 362 | check_decode "\x5f\x00\xff", "".b 363 | }.should raise_error(MessagePack::MalformedFormatError) 364 | end 365 | 366 | it "bare break" do 367 | lambda { 368 | check_decode "\xff", "".b 369 | }.should raise_error(MessagePack::MalformedFormatError) 370 | lambda { 371 | check_decode "\x82\xff\x00\x00", "".b 372 | }.should raise_error(MessagePack::MalformedFormatError) 373 | lambda { 374 | check_decode "\x82\x9f\xff\xff", "".b 375 | }.should raise_error(MessagePack::MalformedFormatError) 376 | end 377 | 378 | it "Tagged" do 379 | expect { check 10, CBOR::Tagged.new("foo", 2) }.to raise_error(TypeError) 380 | check 2, CBOR::Tagged.new(10, 2) 381 | check 4, CBOR::Tagged.new(0xBEEF, 2) 382 | check 6, CBOR::Tagged.new(0xDEADBEEF, 2) 383 | check 10, CBOR::Tagged.new(0xDEADBEEFDEADBEEF, 2) 384 | expect { check 10, CBOR::Tagged.new(0x1DEADBEEFDEADBEEF, 2) }.to raise_error(RangeError) 385 | end 386 | 387 | it "Time" do 388 | check_decode "\xc1\x19\x12\x67", Time.at(4711) 389 | check 6, Time.at(Time.now.to_i) 390 | end 391 | 392 | it "URI" do 393 | # check_decode "\xd8\x20\x78\x13http://www.ietf.org", CBOR::Tagged.new(32, "http://www.ietf.org") 394 | require 'uri' 395 | check_decode "\xd8\x20\x78\x13http://www.ietf.org", URI.parse("http://www.ietf.org") 396 | # This doesn't work yet if 'uri' is not required before 'cbor': 397 | # check 6, URI.parse("http://www.ietf.org") 398 | end 399 | 400 | it "regexp" do 401 | check_decode "\xd8\x23\x63foo", /foo/ 402 | check 14, /(?-mix:foo)/ 403 | check 6, /foo/ 404 | end 405 | 406 | it "Unknown simple 0" do 407 | check_decode "\xf3", CBOR::Simple.new(19) 408 | check 1, CBOR::Simple.new(0) 409 | end 410 | 411 | it "Unknown tag 0" do 412 | check_decode "\xc0\x00", CBOR::Tagged.new(0, 0) 413 | check 11, CBOR::Tagged.new(4711, "Frotzel") 414 | end 415 | 416 | it "Keys as Symbols" do # Experimental! 417 | CBOR.decode(CBOR.encode({:a => 1}), :keys_as_symbols).should == {:a => 1} 418 | CBOR.decode(CBOR.encode({:a => 1})).should == {"a" => 1} 419 | expect { CBOR.decode("\x00", :foobar) }.to raise_error(ArgumentError) 420 | end 421 | 422 | it 'CBOR.decode symbolize_keys' do 423 | symbolized_hash = {:a => 'b', :c => 'd'} 424 | CBOR.decode(CBOR.encode(symbolized_hash), :symbolize_keys => true).should == symbolized_hash 425 | end 426 | 427 | it 'Unpacker#read symbolize_keys' do 428 | unpacker = Unpacker.new(:symbolize_keys => true) 429 | symbolized_hash = {:a => 'b', :c => 'd'} 430 | unpacker.feed(CBOR.encode(symbolized_hash)).read.should == symbolized_hash 431 | end 432 | 433 | it 'handle outrageous sizes 1' do 434 | expect { CBOR.decode("\xa1") }.to raise_error(EOFError) 435 | expect { CBOR.decode("\xba\xff\xff\xff\xff") }.to raise_error(EOFError) 436 | expect { CBOR.decode("\xbb\xff\xff\xff\xff\xff\xff\xff\xff") }.to raise_error(EOFError) 437 | expect { CBOR.decode("\xbb\x01\x01\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 438 | expect { CBOR.decode("\xbb\x00\x00\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 439 | expect { CBOR.decode("\x81") }.to raise_error(EOFError) 440 | expect { CBOR.decode("\x9a\xff\xff\xff\xff") }.to raise_error(EOFError) 441 | end 442 | it 'handle outrageous sizes 2' do 443 | expect { CBOR.decode("\x9b\xff\xff\xff\xff\xff\xff\xff\xff") }.to raise_error(EOFError) 444 | end 445 | it 'handle outrageous sizes 3' do 446 | expect { CBOR.decode("\x9b\x01\x01\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 447 | end 448 | it 'handle outrageous sizes 4' do 449 | expect { CBOR.decode("\x9b\x00\x00\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 450 | expect { CBOR.decode("\x61") }.to raise_error(EOFError) 451 | expect { CBOR.decode("\x7a\xff\xff\xff\xff") }.to raise_error(EOFError) 452 | end 453 | it 'handle outrageous sizes 5' do 454 | expect { CBOR.decode("\x7b\xff\xff\xff\xff\xff\xff\xff\xff") }.to raise_error(EOFError) 455 | end 456 | it 'handle outrageous sizes 6' do 457 | expect { CBOR.decode("\x7b\x01\x01\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 458 | end 459 | it 'handle outrageous sizes 7' do 460 | expect { CBOR.decode("\x7b\x00\x00\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 461 | expect { CBOR.decode("\x41") }.to raise_error(EOFError) 462 | expect { CBOR.decode("\x5a\xff\xff\xff\xff") }.to raise_error(EOFError) 463 | end 464 | it 'handle outrageous sizes 8' do 465 | expect { CBOR.decode("\x5b\xff\xff\xff\xff\xff\xff\xff\xff") }.to raise_error(EOFError) 466 | end 467 | it 'handle outrageous sizes 9' do 468 | expect { CBOR.decode("\x5b\x01\x01\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 469 | end 470 | it 'handle outrageous sizes 10' do 471 | expect { CBOR.decode("\x5b\x00\x00\x01\x01\x01\x01\x01\x01") }.to raise_error(EOFError) 472 | end 473 | 474 | 475 | ## FIXME 476 | # it "{0=>0, 1=>1, ..., 14=>14}" do 477 | # a = (0..14).to_a; 478 | # match Hash[*a.zip(a).flatten], "\x8f\x05\x05\x0b\x0b\x00\x00\x06\x06\x0c\x0c\x01\x01\x07\x07\x0d\x0d\x02\x02\x08\x08\x0e\x0e\x03\x03\x09\x09\x04\x04\x0a\x0a" 479 | # end 480 | # 481 | # it "{0=>0, 1=>1, ..., 15=>15}" do 482 | # a = (0..15).to_a; 483 | # match Hash[*a.zip(a).flatten], "\xde\x00\x10\x05\x05\x0b\x0b\x00\x00\x06\x06\x0c\x0c\x01\x01\x07\x07\x0d\x0d\x02\x02\x08\x08\x0e\x0e\x03\x03\x09\x09\x0f\x0f\x04\x04\x0a\x0a" 484 | # end 485 | 486 | ## FIXME 487 | # it "fixmap" do 488 | # check_map 1, 0 489 | # check_map 1, (1<<4)-1 490 | # end 491 | # 492 | # it "map 16" do 493 | # check_map 3, (1<<4) 494 | # check_map 3, (1<<16)-1 495 | # end 496 | # 497 | # it "map 32" do 498 | # check_map 5, (1<<16) 499 | # #check_map 5, (1<<32)-1 # memory error 500 | # end 501 | 502 | def check(len, obj) 503 | raw = obj.to_cbor.to_s 504 | raw.length.should == len 505 | obj2 = MessagePack.unpack(raw) 506 | obj2.should == obj 507 | if obj.respond_to? :encoding 508 | obj2.encoding.should == obj.encoding 509 | end 510 | end 511 | 512 | def check_raw(overhead, num) 513 | check num+overhead, " "*num 514 | end 515 | 516 | def check_array(overhead, num) 517 | check num+overhead, Array.new(num) 518 | end 519 | 520 | def check_bn(bn, tag) 521 | bnb = bignum_to_bytes(bn < 0 ? ~bn : bn) 522 | bnbs = bnb.size 523 | match bn, "#{tag.chr}#{(bnbs + 0x40).chr}#{bnb}" 524 | end 525 | 526 | def match(obj, buf) 527 | raw = obj.to_cbor.to_s 528 | raw.should == buf 529 | end 530 | 531 | def check_decode(c, obj) 532 | obj2 = MessagePack.unpack(c) 533 | obj2.should == obj 534 | if obj.respond_to? :encoding 535 | obj2.encoding.should == obj.encoding 536 | end 537 | end 538 | 539 | end 540 | 541 | -------------------------------------------------------------------------------- /spec/packer_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: ascii-8bit 2 | require 'spec_helper' 3 | 4 | require 'stringio' 5 | if defined?(Encoding) 6 | Encoding.default_external = 'ASCII-8BIT' 7 | end 8 | 9 | describe Packer do 10 | let :packer do 11 | Packer.new 12 | end 13 | 14 | it 'initialize' do 15 | Packer.new 16 | Packer.new(nil) 17 | Packer.new(StringIO.new) 18 | Packer.new({}) 19 | Packer.new(StringIO.new, {}) 20 | end 21 | 22 | #it 'Packer' do 23 | # Packer(packer).object_id.should == packer.object_id 24 | # Packer(nil).class.should == Packer 25 | # Packer('').class.should == Packer 26 | # Packer('initbuf').to_s.should == 'initbuf' 27 | #end 28 | 29 | it 'write' do 30 | packer.write([]) 31 | packer.to_s.should == "\x80" 32 | end 33 | 34 | it 'write_nil' do 35 | packer.write_nil 36 | packer.to_s.should == "\xf6" 37 | end 38 | 39 | it 'write_array_header 0' do 40 | packer.write_array_header(0) 41 | packer.to_s.should == "\x80" 42 | end 43 | 44 | it 'write_array_header 1' do 45 | packer.write_array_header(1) 46 | packer.to_s.should == "\x81" 47 | end 48 | 49 | it 'write_map_header 0' do 50 | packer.write_map_header(0) 51 | packer.to_s.should == "\xa0" 52 | end 53 | 54 | it 'write_map_header 1' do 55 | packer.write_map_header(1) 56 | packer.to_s.should == "\xa1" 57 | end 58 | 59 | it 'flush' do 60 | io = StringIO.new 61 | pk = Packer.new(io) 62 | pk.write_nil 63 | pk.flush 64 | pk.to_s.should == '' 65 | io.string.should == "\xf6" 66 | end 67 | 68 | it 'buffer' do 69 | o1 = packer.buffer.object_id 70 | packer.buffer << 'frsyuki' 71 | packer.buffer.to_s.should == 'frsyuki' 72 | packer.buffer.object_id.should == o1 73 | end 74 | 75 | it 'to_cbor returns String' do 76 | nil.to_cbor.class.should == String 77 | true.to_cbor.class.should == String 78 | false.to_cbor.class.should == String 79 | 1.to_cbor.class.should == String 80 | 1.0.to_cbor.class.should == String 81 | "".to_cbor.class.should == String 82 | Hash.new.to_cbor.class.should == String 83 | Array.new.to_cbor.class.should == String 84 | end 85 | 86 | class CustomPack01 87 | def to_cbor(pk=nil) 88 | return MessagePack.pack(self, pk) unless pk.class == MessagePack::Packer 89 | pk.write_array_header(2) 90 | pk.write(1) 91 | pk.write(2) 92 | return pk 93 | end 94 | end 95 | 96 | class CustomPack02 97 | def to_cbor(pk=nil) 98 | [1,2].to_cbor(pk) 99 | end 100 | end 101 | 102 | it 'calls custom to_cbor method' do 103 | MessagePack.pack(CustomPack01.new).should == [1,2].to_cbor 104 | MessagePack.pack(CustomPack02.new).should == [1,2].to_cbor 105 | CustomPack01.new.to_cbor.should == [1,2].to_cbor 106 | CustomPack02.new.to_cbor.should == [1,2].to_cbor 107 | end 108 | 109 | it 'calls custom to_cbor method with io' do 110 | s01 = StringIO.new 111 | MessagePack.pack(CustomPack01.new, s01) 112 | s01.string.should == [1,2].to_cbor 113 | 114 | s02 = StringIO.new 115 | MessagePack.pack(CustomPack02.new, s02) 116 | s02.string.should == [1,2].to_cbor 117 | 118 | s03 = StringIO.new 119 | CustomPack01.new.to_cbor(s03) 120 | s03.string.should == [1,2].to_cbor 121 | 122 | s04 = StringIO.new 123 | CustomPack02.new.to_cbor(s04) 124 | s04.string.should == [1,2].to_cbor 125 | end 126 | end 127 | 128 | -------------------------------------------------------------------------------- /spec/random_compat.rb: -------------------------------------------------------------------------------- 1 | 2 | unless defined? Random 3 | class Random 4 | def initialize(seed=Time.now.to_i) 5 | Kernel.srand(seed) 6 | @seed = seed 7 | end 8 | 9 | attr_reader :seed 10 | 11 | def rand(arg) 12 | Kernel.rand(arg) 13 | end 14 | 15 | def bytes(n) 16 | array = [] 17 | n.times do 18 | array << rand(256) 19 | end 20 | array.pack('C*') 21 | end 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' # for 1.8 2 | 3 | RSpec.configure do |config| 4 | config.expect_with :rspec do |c| 5 | c.syntax = [:should, :expect] 6 | end 7 | config.mock_with :rspec do |c| 8 | c.syntax = [:should, :expect] 9 | end 10 | end 11 | 12 | 13 | class String 14 | if ''.respond_to? :encode 15 | def encode_as_utf8 16 | encode(Encoding::UTF_8) 17 | end 18 | def force_as_utf8 19 | force_encoding(Encoding::UTF_8) 20 | end 21 | unless String.instance_methods.include?(:b) 22 | def b 23 | dup.force_encoding(Encoding::BINARY) 24 | end 25 | end 26 | else 27 | def encode_as_utf8 28 | self # MRI 1.8 29 | end 30 | def force_as_utf8 31 | self # MRI 1.8 32 | end 33 | def b 34 | self 35 | end 36 | end 37 | unless ''.respond_to? :clear 38 | def clear 39 | replace('') 40 | end 41 | end 42 | def hexbytes(sep = '') 43 | bytes.map{|x| "%02x" % x}.join(sep) 44 | end 45 | end 46 | 47 | if ENV['SIMPLE_COV'] 48 | require 'simplecov' 49 | SimpleCov.start do 50 | add_filter 'spec/' 51 | add_filter 'pkg/' 52 | add_filter 'vendor/' 53 | end 54 | end 55 | 56 | if ENV['GC_STRESS'] 57 | puts "enable GC.stress" 58 | GC.stress = true 59 | end 60 | 61 | require 'cbor' 62 | 63 | MessagePack = CBOR # XXX 64 | 65 | Packer = MessagePack::Packer 66 | Unpacker = MessagePack::Unpacker 67 | Buffer = MessagePack::Buffer 68 | 69 | -------------------------------------------------------------------------------- /spec/unpacker_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: ascii-8bit 2 | require 'spec_helper' 3 | 4 | describe Unpacker do 5 | let :unpacker do 6 | Unpacker.new 7 | end 8 | 9 | let :packer do 10 | Packer.new 11 | end 12 | 13 | # TODO initialize 14 | 15 | it 'read_array_header succeeds' do 16 | unpacker.feed("\x81") 17 | unpacker.read_array_header.should == 1 18 | end 19 | 20 | it 'read_array_header fails' do 21 | unpacker.feed("\xa1") 22 | lambda { 23 | unpacker.read_array_header 24 | }.should raise_error(MessagePack::TypeError) 25 | end 26 | 27 | it 'read_array_header converts an array to value sequence' do 28 | packer.write_array_header(2) 29 | packer.write("e") 30 | packer.write(1) 31 | unpacker = Unpacker.new 32 | unpacker.feed(packer.to_s) 33 | unpacker.read_array_header.should == 2 34 | unpacker.read.should == "e" 35 | unpacker.read.should == 1 36 | end 37 | 38 | it 'read_map_header succeeds' do 39 | unpacker.feed("\xa1") 40 | unpacker.read_map_header.should == 1 41 | end 42 | 43 | it 'read_map_header converts an map to key-value sequence' do 44 | packer.write_map_header(1) 45 | packer.write("k") 46 | packer.write("v") 47 | unpacker = Unpacker.new 48 | unpacker.feed(packer.to_s) 49 | unpacker.read_map_header.should == 1 50 | unpacker.read.should == "k" 51 | unpacker.read.should == "v" 52 | end 53 | 54 | it 'read_map_header fails' do 55 | unpacker.feed("\x81") 56 | lambda { 57 | unpacker.read_map_header 58 | }.should raise_error(MessagePack::TypeError) 59 | end 60 | 61 | it 'skip_nil succeeds' do 62 | unpacker.feed("\xf6") 63 | unpacker.skip_nil.should == true 64 | end 65 | 66 | it 'skip_nil fails' do 67 | unpacker.feed("\x80") 68 | unpacker.skip_nil.should == false 69 | end 70 | 71 | it 'skip skips objects' do 72 | packer.write(1) 73 | packer.write(2) 74 | packer.write(3) 75 | packer.write(4) 76 | packer.write(5) 77 | 78 | unpacker = Unpacker.new(packer.buffer) 79 | 80 | unpacker.read.should == 1 81 | unpacker.skip 82 | unpacker.read.should == 3 83 | unpacker.skip 84 | unpacker.read.should == 5 85 | end 86 | 87 | it 'read raises EOFError' do 88 | lambda { 89 | unpacker.read 90 | }.should raise_error(EOFError) 91 | end 92 | 93 | it 'skip raises EOFError' do 94 | lambda { 95 | unpacker.skip 96 | }.should raise_error(EOFError) 97 | end 98 | 99 | it 'skip_nil raises EOFError' do 100 | lambda { 101 | unpacker.skip_nil 102 | }.should raise_error(EOFError) 103 | end 104 | 105 | let :sample_object do 106 | [1024, {["a","b"]=>["c","d"]}, ["e","f"], "d", 70000, 4.12, 1.5, 1.5, 1.5] 107 | end 108 | 109 | it 'sample object OK' do 110 | obj2 = MessagePack.unpack(sample_object.to_cbor) 111 | obj2.should == sample_object 112 | end 113 | 114 | it 'feed and each continue internal state' do 115 | raw = sample_object.to_cbor.to_s * 4 116 | objects = [] 117 | 118 | raw.split(//).each do |b| 119 | unpacker.feed(b) 120 | unpacker.each {|c| 121 | objects << c 122 | } 123 | end 124 | 125 | objects.should == [sample_object] * 4 126 | end 127 | 128 | it 'feed_each continues internal state' do 129 | raw = sample_object.to_cbor.to_s * 4 130 | objects = [] 131 | 132 | raw.split(//).each do |b| 133 | unpacker.feed_each(b) {|c| 134 | objects << c 135 | } 136 | end 137 | 138 | objects.should == [sample_object] * 4 139 | end 140 | 141 | it 'reset clears internal buffer' do 142 | # 1-element array 143 | unpacker.feed("\x91") 144 | unpacker.reset 145 | unpacker.feed("\x01") 146 | 147 | unpacker.each.map {|x| x }.should == [1] 148 | end 149 | 150 | it 'reset clears internal state' do 151 | # 1-element array 152 | unpacker.feed("\x91") 153 | unpacker.each.map {|x| x }.should == [] 154 | 155 | unpacker.reset 156 | 157 | unpacker.feed("\x01") 158 | unpacker.each.map {|x| x }.should == [1] 159 | end 160 | 161 | it 'buffer' do 162 | o1 = unpacker.buffer.object_id 163 | unpacker.buffer << 'frsyuki' 164 | unpacker.buffer.to_s.should == 'frsyuki' 165 | unpacker.buffer.object_id.should == o1 166 | end 167 | 168 | it 'frozen short strings' do 169 | raw = sample_object.to_cbor.to_s.force_as_utf8 170 | lambda { 171 | unpacker.feed_each(raw.freeze) { } 172 | }.should_not raise_error 173 | end 174 | 175 | it 'frozen long strings' do 176 | raw = (sample_object.to_cbor.to_s * 10240).force_as_utf8 177 | lambda { 178 | unpacker.feed_each(raw.freeze) { } 179 | }.should_not raise_error 180 | end 181 | 182 | it 'read raises level stack too deep error' do 183 | 512.times { packer.write_array_header(1) } 184 | packer.write(nil) 185 | 186 | unpacker = Unpacker.new(packer.buffer) 187 | lambda { 188 | unpacker.read 189 | }.should raise_error(MessagePack::StackError) 190 | end 191 | 192 | it 'skip raises level stack too deep error' do 193 | 512.times { packer.write_array_header(1) } 194 | packer.write(nil) 195 | 196 | unpacker = Unpacker.new(packer.buffer) 197 | lambda { 198 | unpacker.skip 199 | }.should raise_error(MessagePack::StackError) 200 | end 201 | 202 | it 'read raises invalid byte error' do 203 | unpacker.feed("\xdf") 204 | lambda { 205 | unpacker.read 206 | }.should raise_error(MessagePack::MalformedFormatError) 207 | end 208 | 209 | it 'skip raises invalid byte error' do 210 | unpacker.feed("\xdf") 211 | lambda { 212 | unpacker.skip 213 | }.should raise_error(MessagePack::MalformedFormatError) 214 | end 215 | 216 | it "gc mark" do 217 | raw = sample_object.to_cbor.to_s * 4 218 | 219 | n = 0 220 | raw.split(//).each do |b| 221 | GC.start 222 | unpacker.feed_each(b) {|o| 223 | GC.start 224 | o.should == sample_object 225 | n += 1 226 | } 227 | GC.start 228 | end 229 | 230 | n.should == 4 231 | end 232 | 233 | it "buffer" do 234 | orig = "a"*32*1024*4 235 | raw = orig.to_cbor.to_s 236 | 237 | n = 655 238 | times = raw.size / n 239 | times += 1 unless raw.size % n == 0 240 | 241 | off = 0 242 | parsed = false 243 | 244 | times.times do 245 | parsed.should == false 246 | 247 | seg = raw[off, n] 248 | off += seg.length 249 | 250 | unpacker.feed_each(seg) {|obj| 251 | parsed.should == false 252 | obj.should == orig 253 | parsed = true 254 | } 255 | end 256 | 257 | parsed.should == true 258 | end 259 | end 260 | 261 | --------------------------------------------------------------------------------