scon 0.0.1

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.
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: []