" (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 . " ArrayedCollection extend [ decodeGSM7Bit [ ^ (OsmoGSM at: #GSMDecoding) decode: self. ] decodeUSSD7Bit [ ^ (OsmoGSM at: #GSMUSSDDecoding) decode: self. ] ] ByteArray extend [ decodeGSM7Bit [ ^ (OsmoGSM at: #GSMDecoding) decode: self. ] decodeUSSD7Bit [ ^ (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." ^ (OsmoGSM at: #GSMEncoding) encode: self. ] asUSSD7Bit [ "I convert a string into a 7bit encoded string. I know about padding rules for USSD messages." ^ (OsmoGSM at: #GSMUSSDEncoding) encode: self. ] ] Object subclass: GSMDecoding [ 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 [ 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 [ 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 [ 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)]. ] ]