xbd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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