scon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 61a64a294cb55566d8184fca83089010a4277ff5
4
+ data.tar.gz: 23577a76d5a336c369e21953a7e6052b30b787bc
5
+ SHA512:
6
+ metadata.gz: f514339fbe3c8403dfa79dbe41f0ea7afdf379951bcea33ce5b9d3677ea1d1064f0172c0dbacb49b5cfb460fa49104c6909fd00c31bf5da956f85ae95d836753
7
+ data.tar.gz: 88e8161cce48e6ba7abda2ef6e8f7f0ebbc4c810694edd41216e3f08f598e2cc7c32d01d94e951594298a3ac3867dfceeb2cdf2b64f5e14a6d86e2be77dd9dc5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in scon.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ module SCON
2
+ module Constants
3
+ DATA = {
4
+ :byte => 0xA0,
5
+ :short => 0xA1,
6
+ :integer => 0xA2,
7
+ :long => 0xA3,
8
+ :float => 0xA4,
9
+ :double => 0xA5,
10
+
11
+ :nil => 0xC0,
12
+ :true => 0xC1,
13
+ :false => 0xC2,
14
+
15
+ :string => 0xD0,
16
+
17
+ :obj_start => 0xFA,
18
+ :obj_end => 0xFB,
19
+ :arr_start => 0xFC,
20
+ :arr_end => 0xFD
21
+ }
22
+ end
23
+ end
@@ -0,0 +1,162 @@
1
+ module SCON
2
+ class Generator
3
+
4
+ def generate! hashorarray
5
+ if hashorarray.is_a? Hash
6
+ return generate_type! hashorarray, :hash
7
+ elsif hashorarray.is_a? Array
8
+ return generate_type! hashorarray, :array
9
+ else
10
+ throw TypeError.new("Only Hash or Array types can be generated!")
11
+ end
12
+ end
13
+
14
+ private
15
+ def generate_type! hash, type
16
+ @unique_keys = {}
17
+ @header_bytes = []
18
+ @body_bytes = []
19
+ @body_bytes << 0xF0 if type == :hash
20
+
21
+ if type == :hash
22
+ generate_keys hash
23
+ elsif type == :array
24
+ generate_keys_array hash
25
+ end
26
+ select_keys
27
+ encode_keys
28
+
29
+ encode_body hash
30
+
31
+ @body_bytes.flatten.pack("c*")
32
+ end
33
+
34
+ # Hash Methods
35
+ def generate_keys hash
36
+ hash.each do |key, value|
37
+ keys = key.to_s
38
+ @unique_keys[keys] ||= 0
39
+ @unique_keys[keys] += 1
40
+
41
+ if value.is_a? Hash
42
+ generate_keys value
43
+ elsif value.is_a? Array
44
+ generate_keys_array value
45
+ end
46
+ end
47
+ end
48
+
49
+ def generate_keys_array array
50
+ array.each do |v|
51
+ generate_keys v if v.is_a? Hash
52
+ end
53
+ end
54
+
55
+ def select_keys
56
+ selected = @unique_keys.select { |k, v| v >= 2 }
57
+ @unique_keys = selected.map { |k, v| k }
58
+ end
59
+
60
+ def encode_keys
61
+ if @unique_keys.length > 0
62
+ @header_bytes << 0xF1
63
+ @unique_keys.each do |key|
64
+ @header_bytes << Conversions.string_bytes(key)
65
+ end
66
+ @body_bytes.unshift(@header_bytes)
67
+ @header_bytes = nil
68
+ end
69
+ end
70
+
71
+ #General Methods
72
+ def encode_body object
73
+ if object.is_a? Hash
74
+ object.each do |k, v|
75
+ encode_value k, v, :hash
76
+ end
77
+ elsif object.is_a? Array
78
+ counter = 0
79
+ object.each_with_index do |v, i|
80
+ encode_value i, v, :array, counter
81
+ counter += 1
82
+ end
83
+ end
84
+ end
85
+
86
+ def encode_value key, value, parent_type, array_counter=0
87
+ instruct, serial = 0, nil
88
+ if value.is_a? Hash
89
+ @body_bytes << Constants::DATA[:obj_start]
90
+ encode_key key, parent_type, array_counter
91
+ value.each do |k, v|
92
+ encode_value k, v, :hash
93
+ end
94
+ @body_bytes << Constants::DATA[:obj_end]
95
+ return
96
+ elsif value.is_a? Array
97
+ @body_bytes << Constants::DATA[:arr_start]
98
+ encode_key key, parent_type, array_counter
99
+ counter = 0
100
+ value.each_with_index do |v, i|
101
+ encode_value i, v, :array, counter
102
+ counter += 1
103
+ end
104
+ @body_bytes << Constants::DATA[:arr_end]
105
+ return
106
+ elsif value.is_a? Fixnum
107
+ instruct, serial = auto_number(value)
108
+ elsif value.is_a? Float
109
+ instruct = Constants::DATA[:float]
110
+ serial = Conversions.float_bytes(value)
111
+ elsif value.is_a? String
112
+ bytes = Conversions.string_bytes(value)
113
+ instruct = bytes[0]
114
+ serial = bytes[1]
115
+ elsif value.is_a?(TrueClass)
116
+ instruct = Constants::DATA[:true]
117
+ elsif value.is_a?(FalseClass)
118
+ instruct = Constants::DATA[:false]
119
+ elsif value.nil?
120
+ instruct = Constants::DATA[:nil]
121
+ end
122
+
123
+ @body_bytes << instruct
124
+ encode_key key, parent_type, array_counter
125
+ @body_bytes << serial unless serial.nil?
126
+ end
127
+
128
+ def encode_key key, parent_type, array_counter
129
+ if parent_type == :hash
130
+ keys = key.to_s
131
+ encode_key_hash keys
132
+ end
133
+ end
134
+
135
+ def encode_key_hash keys
136
+ if @unique_keys.include? keys
137
+ index = @unique_keys.index(keys)
138
+ @body_bytes << auto_number(index)
139
+ else
140
+ @body_bytes << Conversions.string_bytes(keys)
141
+ end
142
+ end
143
+
144
+ def auto_number value
145
+ returnval = []
146
+ if value <= 0x99 && value >= 0 # Byte, no following data bytes
147
+ returnval[0] = value
148
+ elsif value <= ((2**16)/2-1) && value >= -(((2**16)/2-1)) # Short, following bytes
149
+ returnval[0] = Constants::DATA[:short]
150
+ returnval[1] = Conversions.short_bytes(value)
151
+ elsif value <= ((2**32)/2-1) && value >= -(((2**32)/2-1)) # Integer, following bytes
152
+ returnval[0] = Constants::DATA[:integer]
153
+ returnval[1] = Conversions.int_bytes(value)
154
+ else # Long, following bytes
155
+ returnval[0] = Constants::DATA[:long]
156
+ returnval[1] = Conversions.long_bytes(value)
157
+ end
158
+ returnval
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,133 @@
1
+ module SCON
2
+ class Parser
3
+
4
+ def parse! data
5
+ @data = data.bytes
6
+ if @data[0] == 0xF1 # Has Key Lookup
7
+ @keyassoc = []
8
+ @data.shift
9
+ while (temp = parse_inline_string(@data)) != false
10
+ @keyassoc << temp
11
+ end
12
+ end
13
+
14
+ if @data[0] == 0xF0 # Is a Hash
15
+ @data.shift
16
+ root = {}
17
+ hash_parse_entries root
18
+ return root
19
+ else # Is an Array
20
+ root = []
21
+ array_parse_entries root
22
+ return root
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def parse_inline_string data
29
+ if (data[0] > 0xD0) && (data[0] < (0xD0 + 32))
30
+ count = data.shift
31
+ return data.shift(count - 0xD0).pack("c*")
32
+ elsif data[0] == 0xD0
33
+ data.shift
34
+ str = []
35
+ while (tmp = data.shift) != 0x03
36
+ str << tmp
37
+ end
38
+ return str.pack("c*")
39
+ else
40
+ return false
41
+ end
42
+ end
43
+
44
+ def parse_string instruct
45
+ if (instruct > 0xD0) && (instruct < (0xD0 + 32))
46
+ return @data.shift(instruct - 0xD0).pack("c*")
47
+ elsif instruct == 0xD0
48
+ str = []
49
+ while (tmp = @data.shift) != 0x03
50
+ str << tmp
51
+ end
52
+ return str.pack("c*")
53
+ else
54
+ return false
55
+ end
56
+ end
57
+
58
+ def parse_value instruct
59
+ if instruct <= 0x99 && instruct >= 0 # Byte, no following data bytes
60
+ return instruct
61
+ elsif instruct == Constants::DATA[:byte]
62
+ return @data.shift[0]
63
+ elsif instruct == Constants::DATA[:short]
64
+ return @data.shift(2).pack("c*").unpack("s<")[0]
65
+ elsif instruct == Constants::DATA[:integer]
66
+ return @data.shift(4).pack("c*").unpack("i<")[0]
67
+ elsif instruct == Constants::DATA[:long]
68
+ return @data.shift(8).pack("c*").unpack("l<")[0]
69
+ elsif instruct == Constants::DATA[:float]
70
+ return @data.shift(4).pack("c*").unpack("e")[0]
71
+ elsif instruct == Constants::DATA[:double]
72
+ return @data.shift(8).pack("c*").unpack("E")[0]
73
+ elsif instruct == Constants::DATA[:nil]
74
+ return nil
75
+ elsif instruct == Constants::DATA[:true]
76
+ return true
77
+ elsif instruct == Constants::DATA[:false]
78
+ return false
79
+ elsif (tmp = parse_string(instruct)) != false
80
+ return tmp
81
+ elsif instruct == Constants::DATA[:obj_start]
82
+ root = {}
83
+ hash_parse_entries root
84
+ return root
85
+ elsif instruct == Constants::DATA[:arr_start]
86
+ root = []
87
+ array_parse_entries root
88
+ return root
89
+ end
90
+ end
91
+
92
+ def hash_parse_entries parent
93
+ closed = false
94
+ while !closed
95
+ data_type = @data.shift
96
+
97
+ if data_type == Constants::DATA[:obj_end]
98
+ closed = true
99
+ next
100
+ end
101
+
102
+ index_meta = @data.shift
103
+ index_val = parse_value index_meta
104
+ data_value = parse_value data_type
105
+
106
+ if index_val.is_a? Fixnum
107
+ parent[@keyassoc[index_val]] = data_value
108
+ elsif index_val.is_a? String
109
+ parent[index_val] = data_value
110
+ end
111
+
112
+ closed = true if @data.length == 0
113
+ end
114
+ end
115
+
116
+ def array_parse_entries parent
117
+ closed = false
118
+ while !closed
119
+ data_type = @data.shift
120
+
121
+ if data_type == Constants::DATA[:arr_end]
122
+ closed = true
123
+ next
124
+ end
125
+ data_value = parse_value data_type
126
+ parent << data_value
127
+
128
+ closed = true if @data.length == 0
129
+ end
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,3 @@
1
+ module SCON
2
+ VERSION = "0.0.1"
3
+ end
data/lib/scon.rb ADDED
@@ -0,0 +1,47 @@
1
+ require "scon/version"
2
+ require "scon/constants"
3
+ require "scon/generator"
4
+ require "scon/parser"
5
+
6
+ module SCON
7
+
8
+ def SCON.generate! hashorarray
9
+ SCON::Generator.new.generate! hashorarray
10
+ end
11
+
12
+ def SCON.parse! data
13
+ SCON::Parser.new.parse! data
14
+ end
15
+
16
+ class Conversions
17
+
18
+ def self.int_bytes int
19
+ [int].pack("i<").bytes
20
+ end
21
+
22
+ def self.short_bytes short
23
+ [short].pack("s<").bytes
24
+ end
25
+
26
+ def self.long_bytes long
27
+ [long].pack("l<").bytes
28
+ end
29
+
30
+ def self.string_bytes str
31
+ bytes = str.bytes
32
+ instr = 0xD0
33
+ if bytes.length < 32
34
+ instr += bytes.length
35
+ else
36
+ bytes << 0x03
37
+ end
38
+ [instr, bytes]
39
+ end
40
+
41
+ def self.float_bytes float
42
+ [float].pack("e").bytes
43
+ end
44
+
45
+ end
46
+
47
+ end
data/scon.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scon/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scon"
8
+ spec.version = SCON::VERSION
9
+ spec.authors = ["JacisNonsense"]
10
+ spec.email = ["jaci.brunning@gmail.com"]
11
+
12
+ spec.summary = %q{Simple and Compressed Object Notation}
13
+ spec.description = %q{Simple and Compressed Object Notation (SCON) serializes arrays
14
+ and objects into a binary format, all the while using as little space as possible by
15
+ reusing duplicate keys with a binary reference and clever type definitions.}
16
+ spec.homepage = "http://www.github.com/JacisNonsense/scon"
17
+
18
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
19
+ # delete this section to allow pushing this gem to any host.
20
+ # if spec.respond_to?(:metadata)
21
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
22
+ # else
23
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
24
+ # end
25
+
26
+ spec.bindir = "bin"
27
+ spec.files = Dir.glob("lib/**/*") + ['Rakefile', 'scon.gemspec', 'Gemfile', 'Rakefile']
28
+ spec.executables = []
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler"
32
+ spec.add_development_dependency "rake"
33
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - JacisNonsense
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: |-
42
+ Simple and Compressed Object Notation (SCON) serializes arrays
43
+ and objects into a binary format, all the while using as little space as possible by
44
+ reusing duplicate keys with a binary reference and clever type definitions.
45
+ email:
46
+ - jaci.brunning@gmail.com
47
+ executables: []
48
+ extensions: []
49
+ extra_rdoc_files: []
50
+ files:
51
+ - Gemfile
52
+ - Rakefile
53
+ - lib/scon.rb
54
+ - lib/scon/constants.rb
55
+ - lib/scon/generator.rb
56
+ - lib/scon/parser.rb
57
+ - lib/scon/version.rb
58
+ - scon.gemspec
59
+ homepage: http://www.github.com/JacisNonsense/scon
60
+ licenses: []
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.4.5
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: Simple and Compressed Object Notation
82
+ test_files: []