Move GSM7Bit and UCS2 into Biju::PDU::Encoding submodule
parent
1c3521586a
commit
2de794042d
|
@ -1,5 +1,5 @@
|
||||||
require 'biju/pdu/gsm7bit'
|
require 'biju/pdu/encoding/gsm7bit'
|
||||||
require 'biju/pdu/ucs2'
|
require 'biju/pdu/encoding/ucs2'
|
||||||
|
|
||||||
require 'biju/pdu/user_data'
|
require 'biju/pdu/user_data'
|
||||||
require 'biju/pdu/data_coding_scheme'
|
require 'biju/pdu/data_coding_scheme'
|
||||||
|
|
|
@ -10,8 +10,8 @@ module Biju
|
||||||
|
|
||||||
def self.autodetect(message)
|
def self.autodetect(message)
|
||||||
message.chars.each do |char|
|
message.chars.each do |char|
|
||||||
return :ucs2 unless GSM7Bit::BASIC_7BIT_CHARACTER_SET.include?(char) ||
|
return :ucs2 unless Encoding::GSM7Bit::BASIC_7BIT_CHARACTER_SET.include?(char) ||
|
||||||
GSM7Bit::BASIC_7BIT_CHARACTER_SET_EXTENSION.has_value?(char)
|
Encoding::GSM7Bit::BASIC_7BIT_CHARACTER_SET_EXTENSION.has_value?(char)
|
||||||
end
|
end
|
||||||
|
|
||||||
:gsm7bit
|
:gsm7bit
|
||||||
|
@ -23,7 +23,7 @@ module Biju
|
||||||
if dcs & 0b11000000 == 0
|
if dcs & 0b11000000 == 0
|
||||||
dcs = DATA_CODING_SCHEME.key(dcs & 0b00001100)
|
dcs = DATA_CODING_SCHEME.key(dcs & 0b00001100)
|
||||||
else
|
else
|
||||||
raise "Unsupported"
|
raise 'Unsupported'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
module Biju
|
||||||
|
module PDU
|
||||||
|
module Encoding
|
||||||
|
class GSM7Bit
|
||||||
|
BASIC_7BIT_CHARACTER_SET = [
|
||||||
|
'@', '£', '$', '¥', 'è', 'é', 'ù', 'ì', 'ò', 'Ç', "\n", 'Ø', 'ø', "\r", 'Å', 'å',
|
||||||
|
"\u0394", '_', "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0","\u03a8", "\u03a3", "\u0398", "\u039e", "\e", 'Æ', 'æ', 'ß', 'É',
|
||||||
|
' ', '!', '"', '#', '¤', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
|
||||||
|
'¡', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||||
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ñ', 'Ü', '§',
|
||||||
|
'¿', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'ä', 'ö', 'ñ', 'ü', 'à'
|
||||||
|
]
|
||||||
|
|
||||||
|
BASIC_7BIT_CHARACTER_SET_EXTENSION = {
|
||||||
|
0x0A => "\n",
|
||||||
|
0x0D => '',
|
||||||
|
0x14 => '^',
|
||||||
|
0x1B => '',
|
||||||
|
0x28 => '{',
|
||||||
|
0x29 => '}',
|
||||||
|
0x2F => '\\',
|
||||||
|
0x3C => '[',
|
||||||
|
0x3D => '~',
|
||||||
|
0x3E => ']',
|
||||||
|
0x40 => '|',
|
||||||
|
0x65 => '€',
|
||||||
|
}
|
||||||
|
|
||||||
|
def self.decode(string, length: 0)
|
||||||
|
res = ''
|
||||||
|
next_char = 0
|
||||||
|
current_length = 0
|
||||||
|
|
||||||
|
string.scan(/../).map(&:hex).each_with_index do |octet, i|
|
||||||
|
index = i % 7
|
||||||
|
current = ((octet & (2 ** (7 - index) - 1)) << index) | next_char
|
||||||
|
|
||||||
|
res = add_char(res, current)
|
||||||
|
current_length += 1
|
||||||
|
|
||||||
|
break if length > 0 && current_length >= length
|
||||||
|
|
||||||
|
next_char = octet >> (7 - index)
|
||||||
|
if index == 6
|
||||||
|
res = add_char(res, next_char)
|
||||||
|
current_length += 1
|
||||||
|
next_char = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.encode(string)
|
||||||
|
res = ''
|
||||||
|
length = 0
|
||||||
|
|
||||||
|
string.chars.each do |char|
|
||||||
|
if get_septet(char)
|
||||||
|
res << get_septet(char).reverse
|
||||||
|
length += 1
|
||||||
|
elsif get_septet(char, escape: true)
|
||||||
|
res << get_septet("\e").reverse
|
||||||
|
res << get_septet(char, escape: true).reverse
|
||||||
|
length += 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
res << ('0' * (8 - (res.length % 8))) unless res.length % 8 == 0
|
||||||
|
|
||||||
|
[
|
||||||
|
res.scan(/.{8}/).map { |octet| '%02x' % octet.reverse.to_i(2) }.join,
|
||||||
|
length: length,
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def self.add_char(string, char)
|
||||||
|
if string[-1] == "\e"
|
||||||
|
string.chop << BASIC_7BIT_CHARACTER_SET_EXTENSION[char]
|
||||||
|
else
|
||||||
|
string << BASIC_7BIT_CHARACTER_SET[char]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_septet(char, escape: false)
|
||||||
|
char = (!escape ? BASIC_7BIT_CHARACTER_SET.index(char) : BASIC_7BIT_CHARACTER_SET_EXTENSION.key(char))
|
||||||
|
|
||||||
|
return nil unless char
|
||||||
|
'%07b' % char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,19 @@
|
||||||
|
module Biju
|
||||||
|
module PDU
|
||||||
|
module Encoding
|
||||||
|
class UCS2
|
||||||
|
def self.decode(string, length: 0)
|
||||||
|
string.scan(/.{4}/).map { |char| char.hex.chr('UCS-2BE') }.join
|
||||||
|
.encode('UTF-8', 'UCS-2BE')
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.encode(string)
|
||||||
|
[
|
||||||
|
string.encode('UCS-2BE').chars.map { |char| "%04x" % char.ord }.join,
|
||||||
|
length: string.length * 2,
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,95 +0,0 @@
|
||||||
module Biju
|
|
||||||
module PDU
|
|
||||||
class GSM7Bit
|
|
||||||
BASIC_7BIT_CHARACTER_SET = [
|
|
||||||
'@', '£', '$', '¥', 'è', 'é', 'ù', 'ì', 'ò', 'Ç', "\n", 'Ø', 'ø', "\r", 'Å', 'å',
|
|
||||||
"\u0394", '_', "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0","\u03a8", "\u03a3", "\u0398", "\u039e", "\e", 'Æ', 'æ', 'ß', 'É',
|
|
||||||
' ', '!', '"', '#', '¤', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
|
|
||||||
'¡', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
||||||
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ñ', 'Ü', '§',
|
|
||||||
'¿', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
|
||||||
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'ä', 'ö', 'ñ', 'ü', 'à'
|
|
||||||
]
|
|
||||||
|
|
||||||
BASIC_7BIT_CHARACTER_SET_EXTENSION = {
|
|
||||||
0x0A => "\n",
|
|
||||||
0x0D => '',
|
|
||||||
0x14 => '^',
|
|
||||||
0x1B => '',
|
|
||||||
0x28 => '{',
|
|
||||||
0x29 => '}',
|
|
||||||
0x2F => '\\',
|
|
||||||
0x3C => '[',
|
|
||||||
0x3D => '~',
|
|
||||||
0x3E => ']',
|
|
||||||
0x40 => '|',
|
|
||||||
0x65 => '€',
|
|
||||||
}
|
|
||||||
|
|
||||||
def self.decode(string, length: 0)
|
|
||||||
res = ''
|
|
||||||
next_char = 0
|
|
||||||
current_length = 0
|
|
||||||
|
|
||||||
string.scan(/../).map(&:hex).each_with_index do |octet, i|
|
|
||||||
index = i % 7
|
|
||||||
current = ((octet & (2 ** (7 - index) - 1)) << index) | next_char
|
|
||||||
|
|
||||||
res = add_char(res, current)
|
|
||||||
current_length += 1
|
|
||||||
|
|
||||||
break if length > 0 && current_length >= length
|
|
||||||
|
|
||||||
next_char = octet >> (7 - index)
|
|
||||||
if index == 6
|
|
||||||
res = add_char(res, next_char)
|
|
||||||
current_length += 1
|
|
||||||
next_char = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.encode(string)
|
|
||||||
res = ''
|
|
||||||
length = 0
|
|
||||||
|
|
||||||
string.chars.each do |char|
|
|
||||||
if get_septet(char)
|
|
||||||
res << get_septet(char).reverse
|
|
||||||
length += 1
|
|
||||||
elsif get_septet(char, escape: true)
|
|
||||||
res << get_septet("\e").reverse
|
|
||||||
res << get_septet(char, escape: true).reverse
|
|
||||||
length += 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
res << ("0" * (8 - (res.length % 8))) unless res.length % 8 == 0
|
|
||||||
|
|
||||||
[
|
|
||||||
res.scan(/.{8}/).map { |octet| "%02x" % octet.reverse.to_i(2) }.join,
|
|
||||||
length: length,
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def self.add_char(string, char)
|
|
||||||
if string[-1] == "\e"
|
|
||||||
string.chop << BASIC_7BIT_CHARACTER_SET_EXTENSION[char]
|
|
||||||
else
|
|
||||||
string << BASIC_7BIT_CHARACTER_SET[char]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.get_septet(char, escape: false)
|
|
||||||
char = (!escape ? BASIC_7BIT_CHARACTER_SET.index(char) : BASIC_7BIT_CHARACTER_SET_EXTENSION.key(char))
|
|
||||||
|
|
||||||
return nil unless char
|
|
||||||
"%07b" % char
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,17 +0,0 @@
|
||||||
module Biju
|
|
||||||
module PDU
|
|
||||||
class UCS2
|
|
||||||
def self.decode(string, length: 0)
|
|
||||||
string.scan(/.{4}/).map { |char| char.hex.chr('UCS-2BE') }.join
|
|
||||||
.encode('UTF-8', 'UCS-2BE')
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.encode(string)
|
|
||||||
[
|
|
||||||
string.encode('UCS-2BE').chars.map { |char| "%04x" % char.ord }.join,
|
|
||||||
length: string.length * 2,
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,8 +2,8 @@ module Biju
|
||||||
module PDU
|
module PDU
|
||||||
class UserData
|
class UserData
|
||||||
ENCODING = {
|
ENCODING = {
|
||||||
gsm7bit: GSM7Bit,
|
gsm7bit: Encoding::GSM7Bit,
|
||||||
ucs2: UCS2,
|
ucs2: Encoding::UCS2,
|
||||||
}
|
}
|
||||||
|
|
||||||
attr_accessor :encoding, :message, :length
|
attr_accessor :encoding, :message, :length
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'biju/pdu/encoding/gsm7bit'
|
||||||
|
|
||||||
|
describe Biju::PDU::Encoding::GSM7Bit do
|
||||||
|
describe '::decode' do
|
||||||
|
it "decodes string" do
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.decode('D4F29C0E', length: 4)).to eq('Test')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "decodes character from extension set" do
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.decode('9B32', length: 2)).to eq('€')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "decodes character with a length of 7" do
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.decode('E170381C0E8701', length: 7)).to eq('a' * 7)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '::encode' do
|
||||||
|
it "encodes string" do
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.encode('Test').first.upcase).to eq('D4F29C0E')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "encodes character from extension set" do
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.encode('€').first.upcase).to eq('9B32')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "encodes character with a length of 7" do
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.encode('a' * 7).first.upcase).to eq('E170381C0E8701')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "gives same text after encoding and decoding" do
|
||||||
|
strings = [
|
||||||
|
'My first TEST',
|
||||||
|
'{More complicated]',
|
||||||
|
'And on€ More~',
|
||||||
|
'a' * 7,
|
||||||
|
]
|
||||||
|
|
||||||
|
strings.each do |string|
|
||||||
|
expect(Biju::PDU::Encoding::GSM7Bit.decode(
|
||||||
|
*Biju::PDU::Encoding::GSM7Bit.encode(string))).to eq(string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,30 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'biju/pdu/encoding/ucs2'
|
||||||
|
|
||||||
|
describe Biju::PDU::Encoding::UCS2 do
|
||||||
|
describe '::decode' do
|
||||||
|
it "decodes string" do
|
||||||
|
expect(Biju::PDU::Encoding::UCS2.decode('00C700E700E200E300E500E4016B00F80153', length: 4)).to eq('Ççâãåäūøœ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '::encode' do
|
||||||
|
it "encodes string" do
|
||||||
|
expect(Biju::PDU::Encoding::UCS2.encode('Ççâãåäūøœ').first.upcase).to eq('00C700E700E200E300E500E4016B00F80153')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "gives same text after encoding and decoding" do
|
||||||
|
strings = [
|
||||||
|
'My first TEST',
|
||||||
|
'{More çomplicated]',
|
||||||
|
'And on€ More~',
|
||||||
|
'þß®',
|
||||||
|
]
|
||||||
|
|
||||||
|
strings.each do |string|
|
||||||
|
expect(Biju::PDU::Encoding::UCS2.decode(
|
||||||
|
*Biju::PDU::Encoding::UCS2.encode(string))).to eq(string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,46 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
require 'biju/pdu/gsm7bit'
|
|
||||||
|
|
||||||
describe Biju::PDU::GSM7Bit do
|
|
||||||
describe '::decode' do
|
|
||||||
it "decodes string" do
|
|
||||||
expect(Biju::PDU::GSM7Bit.decode('D4F29C0E', length: 4)).to eq('Test')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "decodes character from extension set" do
|
|
||||||
expect(Biju::PDU::GSM7Bit.decode('9B32', length: 2)).to eq('€')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "decodes character with a length of 7" do
|
|
||||||
expect(Biju::PDU::GSM7Bit.decode('E170381C0E8701', length: 7)).to eq('a' * 7)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '::encode' do
|
|
||||||
it "encodes string" do
|
|
||||||
expect(Biju::PDU::GSM7Bit.encode('Test').first.upcase).to eq('D4F29C0E')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "encodes character from extension set" do
|
|
||||||
expect(Biju::PDU::GSM7Bit.encode('€').first.upcase).to eq('9B32')
|
|
||||||
end
|
|
||||||
|
|
||||||
it "encodes character with a length of 7" do
|
|
||||||
expect(Biju::PDU::GSM7Bit.encode('a' * 7).first.upcase).to eq('E170381C0E8701')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "gives same text after encoding and decoding" do
|
|
||||||
strings = [
|
|
||||||
'My first TEST',
|
|
||||||
'{More complicated]',
|
|
||||||
'And on€ More~',
|
|
||||||
'a' * 7,
|
|
||||||
]
|
|
||||||
|
|
||||||
strings.each do |string|
|
|
||||||
expect(Biju::PDU::GSM7Bit.decode(
|
|
||||||
*Biju::PDU::GSM7Bit.encode(string))).to eq(string)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,30 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
require 'biju/pdu/ucs2'
|
|
||||||
|
|
||||||
describe Biju::PDU::UCS2 do
|
|
||||||
describe '::decode' do
|
|
||||||
it "decodes string" do
|
|
||||||
expect(Biju::PDU::UCS2.decode('00C700E700E200E300E500E4016B00F80153', length: 4)).to eq('Ççâãåäūøœ')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '::encode' do
|
|
||||||
it "encodes string" do
|
|
||||||
expect(Biju::PDU::UCS2.encode('Ççâãåäūøœ').first.upcase).to eq('00C700E700E200E300E500E4016B00F80153')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "gives same text after encoding and decoding" do
|
|
||||||
strings = [
|
|
||||||
'My first TEST',
|
|
||||||
'{More çomplicated]',
|
|
||||||
'And on€ More~',
|
|
||||||
'þß®',
|
|
||||||
]
|
|
||||||
|
|
||||||
strings.each do |string|
|
|
||||||
expect(Biju::PDU::UCS2.decode(
|
|
||||||
*Biju::PDU::UCS2.encode(string))).to eq(string)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in New Issue