207 lines
6.5 KiB
Smalltalk
207 lines
6.5 KiB
Smalltalk
"
|
|
(C) 2011 by Holger Hans Peter Freyther
|
|
All Rights Reserved
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
"
|
|
|
|
ArrayedCollection extend [
|
|
decodeGSM7Bit [
|
|
<category: '*OsmoGSM-coding'>
|
|
^ (OsmoGSM at: #GSMDecoding) decode: self.
|
|
]
|
|
|
|
decodeUSSD7Bit [
|
|
<category: '*OsmoGSM-coding'>
|
|
^ (OsmoGSM at: #GSMUSSDDecoding) decode: self.
|
|
]
|
|
]
|
|
|
|
ByteArray extend [
|
|
decodeGSM7Bit [
|
|
<category: '*OsmoGSM-coding'>
|
|
^ (OsmoGSM at: #GSMDecoding) decode: self.
|
|
]
|
|
|
|
decodeUSSD7Bit [
|
|
<category: '*OsmoGSM-coding'>
|
|
^ (OsmoGSM at: #GSMUSSDDecoding) decode: self.
|
|
]
|
|
]
|
|
|
|
String extend [
|
|
asGSM7Bit [
|
|
"I convert a string into a 7bit encoded string. I should
|
|
also be in UnicodeString but I am not. This impl. is just
|
|
basic and does not deal with difficult bits."
|
|
<category: '*OsmoGSM-coding'>
|
|
^ (OsmoGSM at: #GSMEncoding) encode: self.
|
|
]
|
|
|
|
asUSSD7Bit [
|
|
"I convert a string into a 7bit encoded string. I know about
|
|
padding rules for USSD messages."
|
|
<category: '*OsmoGSM-coding'>
|
|
^ (OsmoGSM at: #GSMUSSDEncoding) encode: self.
|
|
]
|
|
]
|
|
|
|
Object subclass: GSMDecoding [
|
|
<category: 'OsmoGSM-Coding'>
|
|
<comment: 'I am the base class for GSM Decoding as of GSM 03.38. I
|
|
can be subclassed to deal with specifics for USSD and other systems.'>
|
|
|
|
GSMDecoding class >> decode: aByteArray [
|
|
| bits bytes |
|
|
bits := self convertFromBytes: aByteArray.
|
|
bytes := self convertToBytes: bits.
|
|
^ self handleBytes: bytes from: bits
|
|
]
|
|
|
|
GSMDecoding class >> expand: aByteArray [
|
|
^self convertToBytes: (self convertFromBytes: aByteArray)
|
|
]
|
|
|
|
GSMDecoding class >> handleBytes: bytes from: bits [
|
|
^ bytes asString
|
|
]
|
|
|
|
GSMDecoding class >> convertFromBytes: aByteArray [
|
|
| bits |
|
|
"We convert the stream into single bits. It is the
|
|
easiest to do it like this."
|
|
|
|
bits := OrderedCollection new.
|
|
aByteArray do: [:each |
|
|
1 to: 8 do: [:pos | bits add: (each bitAt: pos)]
|
|
].
|
|
|
|
^ bits
|
|
]
|
|
|
|
GSMDecoding class >> convertToBytes: bits [
|
|
| bytes |
|
|
|
|
bytes := ByteArray new: (bits size // 7).
|
|
1 to: bits size by: 7 do: [:pos |
|
|
(pos + 6 <= bits size) ifTrue: [ | byte |
|
|
byte := 0.
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 6).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 5).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 4).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 3).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 2).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 1).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: pos + 0).
|
|
bytes at: (pos // 7) + 1 put: byte.
|
|
].
|
|
].
|
|
|
|
^ bytes
|
|
]
|
|
]
|
|
|
|
GSMDecoding subclass: GSMUSSDDecoding [
|
|
<category: 'OsmoGSM-Coding'>
|
|
<comment: 'I know funky rules of which characters to remove'>
|
|
|
|
GSMUSSDDecoding class >> handleBytes: bytes from: bits [
|
|
((bytes last = Character cr value) and: [(bits size \\ 7) = 0])
|
|
ifTrue: [ ^ bytes allButLast asString ]
|
|
ifFalse: [ ^ super handleBytes: bytes from: bits ].
|
|
]
|
|
]
|
|
|
|
Object subclass: GSMEncoding [
|
|
<category: 'OsmoGSM-Coding'>
|
|
<comment: 'I am the base class for GSM Encoding as of GSM 03.38. I
|
|
can be subclassed to deal with specifics for USSD and other systems.'>
|
|
|
|
GSMEncoding class >> encode: aString [
|
|
| bits |
|
|
bits := self convertToBits: aString.
|
|
self padBits: bits on: aString.
|
|
^ self convertToBytes: bits.
|
|
]
|
|
|
|
GSMEncoding class >> padBits: bits on: aString [
|
|
"I have to make sure that bits can be divided by/8"
|
|
| rest |
|
|
rest := 8 - (bits size \\ 8).
|
|
rest to: 1 by: -1 do: [:each | bits add: 0].
|
|
]
|
|
|
|
GSMEncoding class >> convertToBits: aString [
|
|
| bits |
|
|
bits := OrderedCollection new: (aString size * 7) // 8.
|
|
|
|
"Split it into bits"
|
|
aString do: [:char | | val |
|
|
val := char value.
|
|
1 to: 7 do: [:digit | bits add: (val bitAt: digit)].
|
|
].
|
|
|
|
^ bits
|
|
]
|
|
|
|
GSMEncoding class >> convertToBytes: bits [
|
|
| bytes |
|
|
|
|
bytes := ByteArray new: bits size // 8.
|
|
1 to: bits size by: 8 do: [:each | | byte |
|
|
byte := 0.
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 7).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 6).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 5).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 4).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 3).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 2).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 1).
|
|
byte := (byte bitShift: 1) bitOr: (bits at: each + 0).
|
|
|
|
bytes at: (each // 8) + 1 put: byte.
|
|
].
|
|
|
|
^ bytes
|
|
]
|
|
]
|
|
|
|
GSMEncoding subclass: GSMUSSDEncoding [
|
|
<category: 'OsmoGSM-Coding'>
|
|
<comment: 'I know the funky stuff for USSD encoding/padding'>
|
|
|
|
GSMUSSDEncoding class >> padBits: bits on: aString [
|
|
| rest nl |
|
|
|
|
"Check if we are on a byte boundary and a CR."
|
|
((aString last = Character cr) and: [(bits size \\ 8) = 0]) ifTrue: [ | cr |
|
|
cr := Character cr value.
|
|
1 to: 7 do: [:each | bits add: (cr bitAt: each) ].
|
|
bits add: 0.
|
|
^ self
|
|
].
|
|
|
|
"Check if we need to handle this, if not continue."
|
|
aString size \\ 8 = 7
|
|
ifFalse: [^ super padBits: bits on: aString].
|
|
|
|
"Now add the padding."
|
|
rest := 8 - (bits size \\ 8).
|
|
rest = 7 ifFalse: [^self error: 'The rest should be 7 bits but were %1' % rest].
|
|
|
|
nl := Character cr value.
|
|
1 to: 7 do: [:each | bits add: (nl bitAt: each)].
|
|
]
|
|
]
|