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 +7 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/lib/scon/constants.rb +23 -0
- data/lib/scon/generator.rb +162 -0
- data/lib/scon/parser.rb +133 -0
- data/lib/scon/version.rb +3 -0
- data/lib/scon.rb +47 -0
- data/scon.gemspec +33 -0
- metadata +82 -0
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
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
|
data/lib/scon/parser.rb
ADDED
@@ -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
|
data/lib/scon/version.rb
ADDED
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: []
|