xbd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in xbd.gemspec
4
+ gemspec
@@ -0,0 +1,25 @@
1
+ XBD Ruby-Gem Copyright (c) 2012, Imikimi LLC
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of the <organization> nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL IMIKIMI LLC BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
@@ -0,0 +1,20 @@
1
+ ## XBD
2
+
3
+ The XBD gem allows you to create, load and save XBD files. XBD files are arbitrary, self-describing, hierarchical,
4
+ binary data structures consisting of "tags", "attributes", and "sub-tags".
5
+
6
+ ## XBD vs XML
7
+
8
+ Feature differences:
9
+
10
+ * Any XML file can be converted to XBD, but the referse is not true.
11
+ * XBD allows you to store aribrary binary strings in attributes.
12
+ * XBD does not allow any data between sub-tags
13
+ * XBD Tag and Attribute names can be arbitrary binary string.
14
+
15
+ Additional benefits of XBD:
16
+
17
+ * XBD uses dictionaries (hashes) to store all Tag-Name, Attribute-Name and Attribute-Values. Consequently: XBD files can be as small as 1/10th the size an equivelent XML file
18
+ * XBD files are simple and streamlined for encoding/decoding. Consequently they can be as much as 10x faster to read and write.
19
+
20
+ (NOTE: The 10x performance improvement was tested on the original pure-C++ implementation vs a fast, pure-C++ XML parser. Tests have not been made in this Ruby version.)
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ # Load custom tasks
4
+ Dir['tasks/*.rake'].sort.each { |f| load f }
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__),"xbd/version.rb")
2
+ require File.join(File.dirname(__FILE__),"xbd/xbd.rb")
@@ -0,0 +1,180 @@
1
+ # encoding: BINARY
2
+ $ruby_inline=true
3
+ require "inline" if $ruby_inline
4
+ require "stringio"
5
+
6
+ module Xbd
7
+ #*********************************
8
+ # Xbd::Asi module
9
+ #*********************************
10
+ #
11
+ # Read and Generate ASI strings
12
+ class Asi
13
+
14
+ # read an ASI from a string, returning an integer
15
+ # optionally starts and the specified offset index.
16
+ #
17
+ # returns the number read and the first index after the ASI data in the string.
18
+ def Asi.read_asi(source,index=0)
19
+ ret=0
20
+ shift=0
21
+ val=0
22
+ while index<source.length
23
+ val=source.byte(index)
24
+ ret+= (val & 0x7F) << shift;
25
+ shift+=7
26
+ index+=1
27
+ break if val<128
28
+ end
29
+ return ret,index
30
+ end
31
+
32
+ def Asi.read_asi_string(source,index=0)
33
+ n,index=read_asi(source,index)
34
+ return source[index,n],index+n
35
+ end
36
+
37
+ def Asi.read_asi_from_file(file)
38
+ ret=0
39
+ shift=0
40
+ val=0
41
+ while val=file.readbyte
42
+ ret+= (val & 0x7F) << shift;
43
+ shift+=7
44
+ break if val<128
45
+ end
46
+ return ret
47
+ end
48
+
49
+ def Asi.asi_to_i(source)
50
+ Asi.read_asi(source,0)[0]
51
+ end
52
+
53
+ # this C function supports all values up to the maximum value of 2^64-1
54
+ # RubyInline autodetects if the number is too big and throws an
55
+ if $ruby_inline
56
+ inline do |compiler|
57
+ compiler.c <<-ENDC
58
+ VALUE i_to_asi_c(unsigned long num) {
59
+ char str[11]; // I think 10 is enough, but just to be safe
60
+ int p=0;
61
+ while(p==0 || num > 0) {
62
+ int byte = (int)(num & 0x7F);
63
+ num = num >> 7;
64
+ if (num > 0) byte = byte | 0x80;
65
+ str[p++]=byte;
66
+ }
67
+ return rb_str_new(str,p);
68
+ }
69
+ ENDC
70
+ end
71
+ else
72
+ def i_to_asi_c(num)
73
+ Asi.i_to_asi_ruby(num)
74
+ end
75
+ end
76
+
77
+ ASI_INSTANCE=Asi.new
78
+ def Asi.i_to_asi2(num)
79
+ ASI_INSTANCE.i_to_asi_c(num)
80
+ end
81
+
82
+ def Asi.i_to_asi_ruby(num)
83
+ ret=""
84
+ while ret.length==0 || num>0
85
+ val=num & 0x7F;
86
+ num=num>>7
87
+ val|=0x80 if num>0
88
+ ret<<val
89
+ end
90
+ ret
91
+ end
92
+ class <<self
93
+ alias :i_to_asi :i_to_asi_ruby
94
+ end
95
+
96
+ def Asi.asi_length(num)
97
+ count=1
98
+ while num>=0x80
99
+ num>>=7
100
+ count+=1
101
+ end
102
+ count
103
+ end
104
+
105
+ #*********************************
106
+ # Enable ASI reading and writing
107
+ # in standard objects.
108
+ #*********************************
109
+ module IO
110
+ def read_asi(index=0)
111
+ Asi.read_asi_from_file(self)
112
+ end
113
+
114
+ # read an asi and then read the next N bytes, where N is the asi value
115
+ # index's value is ignored
116
+ def read_asi_string(index=0)
117
+ read(Asi.read_asi_from_file(self))
118
+ end
119
+ end
120
+
121
+ module Fixnum
122
+ ASI_INSTANCE = Asi.new
123
+ def to_asi
124
+ ASI_INSTANCE.i_to_asi_c(self)
125
+ end
126
+ def asi_length
127
+ Xbd::Asi.asi_length(self)
128
+ end
129
+ end
130
+
131
+ module Bignum
132
+ def to_asi
133
+ Asi.i_to_asi(self)
134
+ end
135
+ def asi_length
136
+ Xbd::Asi.asi_length(self)
137
+ end
138
+ end
139
+
140
+ module String
141
+ def from_asi
142
+ Asi.asi_to_i(self)
143
+ end
144
+
145
+ def to_asi_string
146
+ self.length.to_asi+self
147
+ end
148
+
149
+ def read_asi(index=0)
150
+ Asi.read_asi(self,index)
151
+ end
152
+
153
+ def read_asi_string(index=0)
154
+ Asi.read_asi_string(self,index)
155
+ end
156
+
157
+ # Ruby 1.8 patch to ignore force_encoding
158
+ if !"".respond_to?(:force_encoding)
159
+ def to_binary; self end
160
+ def force_encoding(a) self end
161
+ def byte(index)
162
+ self[index]
163
+ end
164
+ else
165
+ # Ruby 1.9
166
+ def to_binary; self.force_encoding("BINARY") end
167
+ def byte(index)
168
+ char=self[index]
169
+ char && char.bytes.next
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ class Fixnum ; include Xbd::Asi::Fixnum ; end
177
+ class File ; include Xbd::Asi::IO ; end
178
+ class StringIO; include Xbd::Asi::IO ; end
179
+ class Bignum ; include Xbd::Asi::Bignum ; end
180
+ class String ; include Xbd::Asi::String ; end
@@ -0,0 +1,3 @@
1
+ module Xbd
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,122 @@
1
+ # encoding: BINARY
2
+ #*****************************************************
3
+ # Ruby XBD Library
4
+ # (C) 2010-10-03 Shane Brinkman-Davis
5
+ #
6
+ # SRC home:
7
+ # URL: https://svn.imikimi.com/auto_branch/2008-10-30_15-54-25_iphone_edge/xbd/xbd.rb
8
+ # Repository Root: https://svn.imikimi.com
9
+ # Repository UUID: d1359c2d-ec2c-0410-b0bc-eca7d5e44040
10
+ # Revision: 11647
11
+ #*****************************************************
12
+ =begin
13
+
14
+ This code read and writes XBD files.
15
+
16
+ To get started:
17
+
18
+ Take any XBD file
19
+ example Kimi from Imikimi.com (all Kimis are XBD containers)
20
+ http://imikimi.com/plugin/get_kimi/o-10i
21
+
22
+ And run:
23
+ require "xbd.rb"
24
+ puts Xbd.load_from_file("your_xbd_filename.xbd")
25
+
26
+ Master verison of this file lives in: imikimi_plugin_source/xbd/xbd.rb
27
+
28
+ =end
29
+ require File.join(File.dirname(__FILE__),"asi")
30
+ require File.join(File.dirname(__FILE__),"xbd_dictionary")
31
+ require File.join(File.dirname(__FILE__),"xbd_tag")
32
+
33
+ #*********************************
34
+ # Xbd Module
35
+ #*********************************
36
+ module Xbd
37
+
38
+ SBDXML_HEADER="SBDXML\1\0"
39
+
40
+ #**********************************************************
41
+ # XML escaping
42
+ #**********************************************************
43
+ # Used when converting TO xml (Xbd::Tag.to_s)
44
+ # Note, some of these codes are actually invalid strict XML because XML has no escape codes for some values.
45
+ # SBD: WTF! (I'm still surprised by this, but it appears to be true.)
46
+ XML_ESCAPE_CODES={
47
+ "&#0;"=>0,
48
+ "&#1;"=>1,
49
+ "&#2;"=>2,
50
+ "&#3;"=>3,
51
+ "&#4;"=>4,
52
+ "&#5;"=>5,
53
+ "&#6;"=>6,
54
+ "&#7;"=>7,
55
+ "&#8;"=>8,
56
+ "&#9;"=>9,
57
+ "&#10;"=>10,
58
+ "&#11;"=>11,
59
+ "&#12;"=>12,
60
+ "&#13;"=>13,
61
+ "&#14;"=>14,
62
+ "&#15;"=>15,
63
+ "&#16;"=>16,
64
+ "&#17;"=>17,
65
+ "&#18;"=>18,
66
+ "&#19;"=>19,
67
+ "&#20;"=>20,
68
+ "&#21;"=>21,
69
+ "&#22;"=>22,
70
+ "&#23;"=>23,
71
+ "&#24;"=>24,
72
+ "&#25;"=>25,
73
+ "&#26;"=>26,
74
+ "&#27;"=>27,
75
+ "&#28;"=>28,
76
+ "&#29;"=>29,
77
+ "&#30;"=>30,
78
+ "&#31;"=>31,
79
+ "&quot;"=>34, #"
80
+ "&amp;"=>38, #&
81
+ "&apos;"=>39, #'
82
+ "&lt;"=>60, #<
83
+ "&gt;"=>62 #>
84
+ }
85
+
86
+ @@escape_for_xml=[]
87
+ (0..255).each {|i| @@escape_for_xml<<i.chr}
88
+ XML_ESCAPE_CODES.each {|k,v| @@escape_for_xml[v]=k}
89
+
90
+ def self.xml_escape(s)
91
+ out=""
92
+ s.each_byte {|b| out<< @@escape_for_xml[b]}
93
+ out
94
+ end
95
+
96
+ # XBD.parse accepts:
97
+ # a string or
98
+ # any object that returns a string in response to the method "read"
99
+ # (for example, an open file handle)
100
+ def Xbd.parse(source)
101
+ #treat source as a stream(file) if it isn't a string
102
+ source=source.read.force_encoding("BINARY") if source.class!=String
103
+
104
+ # read the header
105
+ raise "Not a valid XBD file" unless source[0..SBDXML_HEADER.length-1]==SBDXML_HEADER
106
+ index=SBDXML_HEADER.length
107
+
108
+ # read each of the 3 dictionaries in order
109
+ tagsd,index=Dictionary.parse(source,index)
110
+ attrsd,index=Dictionary.parse(source,index)
111
+ valuesd,index=Dictionary.parse(source,index)
112
+
113
+ # read all tags, return the root-tag
114
+ Tag.parse(source,index,tagsd,attrsd,valuesd)[0]
115
+ end
116
+
117
+ # Load XBD from filename
118
+ def Xbd.load_from_file(filename)
119
+ Xbd.parse(File.open(filename,"rb"))
120
+ end
121
+
122
+ end
@@ -0,0 +1,52 @@
1
+ module Xbd
2
+ #*********************************
3
+ # Xbd::Dictionary
4
+ #*********************************
5
+ # Consists of:
6
+ # @hash: a map from values to IDs and IDs to values
7
+ # @array: a list of values; their indexes == their IDs
8
+ #
9
+ class Dictionary
10
+ attr_reader :hash,:array
11
+
12
+ def initialize(initial_values=[])
13
+ @hash={}
14
+ @array=[]
15
+ initial_values.each {|v| self<<(v)}
16
+ end
17
+
18
+ # return String given an ID, or ID given a String
19
+ def [](i) @hash[i] end
20
+
21
+ def Dictionary.sanitize_string(str)
22
+ case str
23
+ when String then "#{str}".force_encoding("BINARY")
24
+ else str.to_s.force_encoding("BINARY")
25
+ end
26
+ end
27
+
28
+ # add a String to the dictionary
29
+ def <<(str)
30
+ str = Dictionary.sanitize_string str
31
+ @hash[str] ||= begin
32
+ new_id = @array.length
33
+ @array << @hash[new_id] = str
34
+ new_id
35
+ end
36
+ end
37
+
38
+ # convert to binary string
39
+ def to_binary
40
+ [@array.length.to_asi, @array.collect{|v| v.length.to_asi}, @array].join.to_asi_string
41
+ end
42
+
43
+ def Dictionary.parse(source,index=0)
44
+ encoded_dictionary, index = source.read_asi_string index
45
+ encoded_dictionary = StringIO.new(encoded_dictionary)
46
+ num_entries = encoded_dictionary.read_asi
47
+ lengths = num_entries.times.collect {encoded_dictionary.read_asi}
48
+ strings = lengths.collect {|len| encoded_dictionary.read len}
49
+ [Dictionary.new(strings), index]
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,187 @@
1
+ module Xbd
2
+ #*********************************
3
+ # Xbd::Tag object
4
+ #*********************************
5
+ #
6
+ # Consists of:
7
+ # name: a string
8
+ # attrs: a Hash of Attributes, String -> String
9
+ # tags: an ordered Array of Tag objects
10
+ class Tag
11
+
12
+ def initialize(name,attrs=nil,tags=nil,&block)
13
+ @name=name.to_s
14
+ @attrs={}
15
+ attrs.each {|k,v|@attrs[k.to_s]=v.to_s} if attrs
16
+ @tags=[]
17
+ self<<tags if tags
18
+ yield self if block
19
+ end
20
+ #************************************************************
21
+ # Access Name
22
+ #************************************************************
23
+ def name() @name end
24
+ def name=(n) @name=n end
25
+
26
+ #************************************************************
27
+ # Access Attrs
28
+ #************************************************************
29
+ attr_reader :attrs
30
+ def [](attr) @attrs[attr.to_s] end
31
+ def []=(attr,val) val==nil ? @attrs.delete(attr.to_s) : @attrs[attr.to_s]=val.to_s end
32
+
33
+ #************************************************************
34
+ # Access Tags
35
+ #************************************************************
36
+ # return tags array
37
+ attr_reader :tags
38
+ def tagnames() @tags.collect {|t| t.name} end
39
+
40
+ # returns first tag that matches name
41
+ # names can be a "/" delimited path string
42
+ # OR an array of exact string values to match (in case you want to match "/" in a tag name)
43
+ def tag(names)
44
+ return self if !names || (names.kind_of?(Array) && names.length==0)
45
+ names=names.split("/") unless names.kind_of?(Array)
46
+ name=names[0]
47
+ tags.each do |tag|
48
+ return tag.tag(names[1..-1]) if tag.name==name
49
+ end
50
+ return nil
51
+ end
52
+
53
+ def each_attribute
54
+ @attrs.each {|k,v| yield k,v}
55
+ end
56
+
57
+ # iterate over all tags or only with matching names
58
+ def each_tag(name=nil)
59
+ tags.each do |tag|
60
+ yield tag if !name || tag.name==name
61
+ end
62
+ end
63
+
64
+ # Add sub-tag or array of sub-tags
65
+ def <<(tag)
66
+ return unless tag # ignore nil
67
+ tags= tag.kind_of?(Array) ? tag : [tag]
68
+ tags.each {|t| raise "All sub-tags in must be #{self.class} objects. Attempted to add #{t.class} object." unless t.kind_of?(self.class)}
69
+ @tags+=tags
70
+ tag
71
+ end
72
+
73
+ def ==(tag)
74
+ name==tag.name &&
75
+ attrs==tag.attrs &&
76
+ tags==tag.tags
77
+ end
78
+
79
+ #************************************************************
80
+ # to XML (to_s)
81
+ #************************************************************
82
+ def to_s(indent="",max_attr_value_length=nil)
83
+ a=[name]
84
+ attrs.keys.sort.each do |k|
85
+ v=attrs[k]
86
+ v=v[0..max_attr_value_length-1] if max_attr_value_length
87
+ a<<"#{k}=\"#{Xbd.xml_escape(v)}\""
88
+ end
89
+ ret="#{indent}<#{a.join(' ')}"
90
+ if tags.length>0
91
+ ret+=">\n"
92
+ tags.each {|st| ret+=st.to_s(indent+" ",max_attr_value_length)}
93
+ ret+="#{indent}</#{name}>\n"
94
+ else
95
+ ret+="/>\n"
96
+ end
97
+ end
98
+ alias :to_xml :to_s
99
+
100
+ def inspect
101
+ to_s("",32)
102
+ end
103
+
104
+ # convert to basic ruby data structure
105
+ def to_ruby
106
+ {:name=>name, :attrs=>@attrs.clone, :tags=>tags.collect {|tag|tag.to_ruby}}
107
+ end
108
+
109
+ #************************************************************
110
+ # to binary XBD support methods
111
+ #************************************************************
112
+ def populate_dictionaries(tagsd,attrsd,valuesd)
113
+ tagsd<<name # add this tag's name
114
+ attrs.each {|k,v| attrsd<<k; valuesd<<v} # add all attribute names and values
115
+ tags.each {|tag| tag.populate_dictionaries(tagsd,attrsd,valuesd)} # recurse on sub-tags
116
+ end
117
+
118
+ # encode just this tag in binary
119
+ # Note this returned value alone is not parsable
120
+ def to_binary_partial(tagsd,attrsd,valuesd)
121
+ # build attrs_data string: all attr name-value pairs as ASIs concatinated
122
+ attrs_data=attrs.keys.sort.collect {|key| attrsd[key].to_asi + valuesd[attrs[key]].to_asi}.join
123
+
124
+ data=tagsd[name].to_asi + # name asi
125
+ attrs_data.length.to_asi + attrs_data + # attrs length asi and attrs
126
+ tags.collect {|tag| tag.to_binary_partial(tagsd,attrsd,valuesd)}.join # sub-tags
127
+ data.to_asi_string # tag data pre-pended with tag-data length asi
128
+ end
129
+
130
+ #************************************************************
131
+ # to binary XBD (to_xbd)
132
+ #************************************************************
133
+ # use this to convert an xbd tag structure into a saveable xbd file-string
134
+ def to_binary
135
+ populate_dictionaries(tagsd=Dictionary.new, attrsd=Dictionary.new, valuesd=Dictionary.new)
136
+ Xbd::SBDXML_HEADER + tagsd.to_binary + attrsd.to_binary + valuesd.to_binary + to_binary_partial(tagsd,attrsd,valuesd)
137
+ end
138
+
139
+ #**********************************************************
140
+ # Load XBD Tag Data from String
141
+ #**********************************************************
142
+ # parse a Tag, all its Attributes and all its Sub-tags recursively.
143
+ #
144
+ # inputs:
145
+ # source - source binary string
146
+ # index - offset to start reading at
147
+ # tagsd - tag-names dictionary
148
+ # attrsd - attribute-names dictionary
149
+ # valuesd - attribute-values dictionary
150
+ # returns the Tag object generated AND the first "index" in the string after read tag-data
151
+ def Tag.parse(source,index,tagsd,attrsd,valuesd)
152
+ tag_length,index=Asi.read_asi(source,index)
153
+ tag_start_index=index
154
+
155
+ # read tag name
156
+ tag_name_id,index=Asi.read_asi(source,index)
157
+ tag_name=tagsd[tag_name_id]
158
+ raise "tag name id(#{tag_name_id}) not in tag-names dictionary" if !tag_name
159
+
160
+ # read attributes
161
+ attr_byte_size,index=Asi.read_asi(source,index)
162
+ attrs_hash={}
163
+ while attr_byte_size>0
164
+ i=index
165
+ name_id,index=Asi.read_asi(source,index)
166
+ value_id,index=Asi.read_asi(source,index)
167
+ attr_byte_size-=(index-i)
168
+ n=attrsd[name_id]
169
+ v=valuesd[value_id]
170
+ raise "attribute name id(#{name_id}) not in attribute-names dictionary" if !n
171
+ raise "attribute value id(#{value_id}) not in attribue-values dictionary" if !v
172
+ attrs_hash[n]=v
173
+ end
174
+ tag_length-=(index-tag_start_index)
175
+
176
+ # read sub-tags
177
+ tags=[]
178
+ while tag_length>0
179
+ i=index
180
+ node,index=Tag.parse(source,index,tagsd,attrsd,valuesd)
181
+ tags<<node
182
+ tag_length-=(index-i)
183
+ end
184
+ return Tag.new(tag_name,attrs_hash,tags),index
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,70 @@
1
+ require File.join(File.dirname(__FILE__),"xbd_test_helper")
2
+
3
+ module Xbd
4
+ describe Asi do
5
+
6
+ def test_asi(n)
7
+ asi1=n.to_asi
8
+ asi2=Asi::ASI_INSTANCE.i_to_asi_c(n)
9
+ raise "(asi1=n.to_asi)!=asi2 (#{asi1.inspect}!=#{asi2.inspect}) n=#{n} asi1.encoding=#{asi1.encoding} asi2.encoding=#{asi2.encoding}" unless asi1==asi2
10
+ asi1.read_asi[0].should == n
11
+ end
12
+
13
+ it "should be possible to convert all powers of two up to 2^64-1" do
14
+ v=0
15
+ 65.times do
16
+ n=2**v-1
17
+ test_asi(n)
18
+ v+=1
19
+ end
20
+ end
21
+
22
+ if $ruby_inline
23
+ it "should fail to convert 2^64 to an asi" do
24
+ lambda {test_asi(2**64)}.should raise_error(RangeError)
25
+ end
26
+ end
27
+
28
+ it "Fixnum > String > Fixnum" do
29
+ test_edges do |num|
30
+ num.to_asi.from_asi.should==num
31
+ end
32
+ end
33
+
34
+ it "to_asi_string and StringIO" do
35
+ test_string="foo"
36
+ asi_foo=test_string.to_asi_string
37
+ StringIO.new(asi_foo).read_asi_string.should==test_string
38
+ end
39
+
40
+ it "to_asi and StringIO" do
41
+ test_edges do |num|
42
+ asi=num.to_asi
43
+ StringIO.new(asi).read_asi.should==num
44
+ end
45
+ end
46
+
47
+ it "to_asi_string and String" do
48
+ test_string="foo"
49
+ asi_foo=test_string.to_asi_string
50
+ asi_foo.read_asi_string.should== [test_string,asi_foo.length]
51
+ end
52
+
53
+ it "asi_length" do
54
+ test_edges do |n,c|
55
+ n.asi_length.should==c
56
+ end
57
+ end
58
+
59
+ def test_edges
60
+ yield 0,1
61
+
62
+ (1..10).each do |c|
63
+ n=(1<<(7*c))
64
+ n_1=n-1
65
+ yield n_1,c
66
+ yield n,c+1
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__),"xbd_test_helper")
2
+
3
+ module Xbd
4
+ describe Dictionary do
5
+ it "should be possible to create a dictionary" do
6
+ Dictionary.new.should_not == nil
7
+ end
8
+
9
+ it "should return nils for non-existant entries" do
10
+ dict=Dictionary.new
11
+ dict["not_there"].should==nil
12
+ end
13
+
14
+ it "should be enumerate words added to it" do
15
+ dict=Dictionary.new
16
+ dict<<"word"
17
+ dict<<"with"
18
+ dict<<"you"
19
+
20
+ dict["word"].should==0
21
+ dict["with"].should==1
22
+ dict["you"].should==2
23
+
24
+ dict[0].should=="word"
25
+ dict[1].should=="with"
26
+ dict[2].should=="you"
27
+ end
28
+
29
+ it "should accept non-string values and convert them to strings" do
30
+ dict=Dictionary.new
31
+ dict<<:word
32
+ dict["word"].should==0
33
+ end
34
+
35
+ it "should work to_bin and parse" do
36
+ dict=Dictionary.new
37
+ dict<<"foo"
38
+ dict<<"bar"
39
+ dict["foo"].should==0
40
+ dict["bar"].should==1
41
+ dict[0].should=="foo"
42
+ dict[1].should=="bar"
43
+
44
+ bin=dict.to_binary
45
+ dict2,next_index=Dictionary.parse(bin)
46
+ next_index.should==bin.length
47
+ dict2["foo"].should==0
48
+ dict2["bar"].should==1
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,132 @@
1
+ require File.join(File.dirname(__FILE__),"xbd_test_helper")
2
+ require "fileutils"
3
+
4
+ module Xbd
5
+ describe Tag do
6
+ it "test the various creation methods" do
7
+ Tag.new("my_tag").attrs.should=={}
8
+ Tag.new("my_tag",{:foo=>:bar}).attrs.should=={"foo"=>"bar"}
9
+ Tag.new("my_tag",{:foo=>:bar},[Tag.new("my_other_tag")]).attrs.should=={"foo"=>"bar"}
10
+ Tag.new("my_tag") do |tag|
11
+ tag[:foo]=:bar
12
+ end.attrs.should=={"foo"=>"bar"}
13
+ end
14
+
15
+ it "test attrs" do
16
+ tag=Tag.new("my_tag")
17
+ tag.attrs.should=={}
18
+ tag[:foo]=:bar
19
+ tag["foo"].should=="bar"
20
+ tag.attrs.should=={"foo"=>"bar"}
21
+ tag[:foo]=:bar2
22
+ tag["foo"].should=="bar2"
23
+ tag.attrs.should=={"foo"=>"bar2"}
24
+ tag[:foo2]=:bar3
25
+ tag["foo2"].should=="bar3"
26
+ tag.attrs.should=={"foo"=>"bar2","foo2"=>"bar3"}
27
+ end
28
+
29
+ it "test tags" do
30
+ tag=full_test_tag
31
+ tag.tagnames.should==["sub1","sub2"]
32
+ tag.tag("sub1")["sub1k"].should==nil
33
+ tag.tag("sub2")["sub2k"].should=="sub2v"
34
+ tag.each_tag("sub1") do |t|
35
+ t.name.should=="sub1"
36
+ end
37
+ end
38
+
39
+ it "should convert to XML" do
40
+ tag=full_test_tag
41
+ tag.tag("sub1").to_s.strip.should=='<sub1/>'
42
+ tag.tag("sub2").to_s.strip.should=='<sub2 sub2k="sub2v"/>'
43
+ tag.to_s.gsub(/\s+/," ").strip.should=='<my_tag a1="v1"> <sub1/> <sub2 sub2k="sub2v"/> </my_tag>'
44
+ tag.inspect
45
+ end
46
+
47
+ it "should convert to Ruby Hashes" do
48
+ tag=full_test_tag
49
+
50
+ tag.to_ruby.should=={
51
+ :name=>"my_tag",
52
+ :attrs=>{"a1"=>"v1"},
53
+ :tags=>[
54
+ {:name=>"sub1", :attrs=>{}, :tags=>[]},
55
+ {:name=>"sub2", :attrs=>{"sub2k"=>"sub2v"}, :tags=>[]}
56
+ ]
57
+ }
58
+ end
59
+
60
+ it "should work to convert to xbd" do
61
+ tag=full_test_tag
62
+ xbd=tag.to_binary
63
+ tag2=Xbd.parse(xbd)
64
+ tag.to_s.should==tag2.to_s
65
+ end
66
+
67
+ it "should work to store binaries" do
68
+ tag1=full_test_tag
69
+ tag1["nested_xbd"]=full_test_tag.to_binary
70
+ tag2=Xbd.parse(tag1.to_binary)
71
+ tag2["nested_xbd"].should==tag1["nested_xbd"]
72
+ Xbd.parse(tag2["nested_xbd"]).should==full_test_tag
73
+ end
74
+
75
+ it "should work to == and !=" do
76
+ tag1=full_test_tag
77
+ tag2=full_test_tag
78
+ (tag1!=tag2).should==false
79
+ (tag1==tag2).should==true
80
+
81
+ # test diff tags
82
+ tag2.tags.reverse!
83
+ (tag1!=tag2).should==true
84
+ (tag1==tag2).should==false
85
+
86
+ # test diff names
87
+ tag2=full_test_tag
88
+ tag2.name="nameeroo"
89
+ (tag1!=tag2).should==true
90
+ (tag1==tag2).should==false
91
+
92
+ # test diff attrs
93
+ tag2=full_test_tag
94
+ tag2["foomagoo"]="goofoo"
95
+ (tag1!=tag2).should==true
96
+ (tag1==tag2).should==false
97
+ end
98
+
99
+ it "should work to load from file" do
100
+ begin
101
+ filename=File.join(File.dirname(__FILE__),"xbd_test_file.xbd")
102
+ tag=full_test_tag
103
+ File.open(filename,"wb") {|file| file.write(tag.to_binary)}
104
+ File.exists?(filename).should==true
105
+ tag2=Xbd.load_from_file(filename)
106
+ tag2.should==tag
107
+ ensure
108
+ FileUtils.rm filename
109
+ end
110
+ end
111
+
112
+ it "should work to convert to/from binary" do
113
+ tagsd=Dictionary.new
114
+ attrsd=Dictionary.new
115
+ valuesd=Dictionary.new
116
+ tag=full_test_tag
117
+ tag.populate_dictionaries(tagsd,attrsd,valuesd)
118
+ bin=tag.to_binary_partial(tagsd,attrsd,valuesd)
119
+ tag2,index=Tag.parse(bin,0,tagsd,attrsd,valuesd)
120
+ index.should==bin.length
121
+ tag.to_s.should==tag2.to_s
122
+ end
123
+
124
+ def full_test_tag
125
+ Tag.new("my_tag") do |tag|
126
+ tag["a1"]="v1"
127
+ tag<<Tag.new("sub1")
128
+ tag<<Tag.new("sub2",{:sub2k=>:sub2v})
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,3 @@
1
+ #require 'simplecov'
2
+ #SimpleCov.start
3
+ require File.join(File.dirname(__FILE__),"../lib/xbd.rb")
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'rspec'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc "Run all examples with RCov"
6
+ RSpec::Core::RakeTask.new('spec:rcov') do |t|
7
+ t.rcov = true
8
+ end
9
+
10
+ RSpec::Core::RakeTask.new('spec') do |t|
11
+ t.verbose = true
12
+ end
13
+
14
+ task :default => :spec
15
+ rescue LoadError
16
+ puts "rspec, or one of its dependencies, is not available. Install it with: sudo gem install rspec"
17
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "xbd/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "xbd"
7
+ s.version = Xbd::VERSION
8
+ s.authors = ["Shane Brinkman-Davis"]
9
+ s.email = ["shanebdavis@imikimi.com"]
10
+ s.homepage = "https://github.com/Imikimi-LLC/xbd"
11
+ s.summary = %q{A fast, simplified, XML-inspired binary file format.}
12
+ s.description = %q{The XBD gem allows you to create, load and save XBD files. XBD files are arbitrary, self-describing, hierarchical, binary data structures consisting of "tags", "attributes", and "sub-tags".}
13
+
14
+ s.rubyforge_project = "xbd"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'RubyInline', '~> 3.11.0'
22
+ s.add_development_dependency 'rake'
23
+ s.add_development_dependency 'rspec', '~> 2.6.0'
24
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xbd
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Shane Brinkman-Davis
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-06-08 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: RubyInline
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 43
29
+ segments:
30
+ - 3
31
+ - 11
32
+ - 0
33
+ version: 3.11.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rspec
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 23
59
+ segments:
60
+ - 2
61
+ - 6
62
+ - 0
63
+ version: 2.6.0
64
+ type: :development
65
+ version_requirements: *id003
66
+ description: The XBD gem allows you to create, load and save XBD files. XBD files are arbitrary, self-describing, hierarchical, binary data structures consisting of "tags", "attributes", and "sub-tags".
67
+ email:
68
+ - shanebdavis@imikimi.com
69
+ executables: []
70
+
71
+ extensions: []
72
+
73
+ extra_rdoc_files: []
74
+
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.TXT
79
+ - README.md
80
+ - Rakefile
81
+ - lib/xbd.rb
82
+ - lib/xbd/asi.rb
83
+ - lib/xbd/version.rb
84
+ - lib/xbd/xbd.rb
85
+ - lib/xbd/xbd_dictionary.rb
86
+ - lib/xbd/xbd_tag.rb
87
+ - spec/xbd_asi_spec.rb
88
+ - spec/xbd_dictionary_spec.rb
89
+ - spec/xbd_tag_spec.rb
90
+ - spec/xbd_test_helper.rb
91
+ - tasks/spec.rake
92
+ - xbd.gemspec
93
+ homepage: https://github.com/Imikimi-LLC/xbd
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements: []
120
+
121
+ rubyforge_project: xbd
122
+ rubygems_version: 1.8.10
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: A fast, simplified, XML-inspired binary file format.
126
+ test_files:
127
+ - spec/xbd_asi_spec.rb
128
+ - spec/xbd_dictionary_spec.rb
129
+ - spec/xbd_tag_spec.rb
130
+ - spec/xbd_test_helper.rb