Compare commits
	
		
			37 Commits
		
	
	
		
			benchmark
			...
			b74a7c842f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b74a7c842f | |||
| d477951fb4 | |||
| 4c92723df0 | |||
| d0c868ca5c | |||
| 7001f2c51a | |||
| 7b4fcf3de1 | |||
| ad8eaff930 | |||
| f847588a24 | |||
| ee4f8ecfd6 | |||
| f5e263752e | |||
| 39c7876ed1 | |||
| b2a588e667 | |||
| db61da9fc2 | |||
| 9fbcdd86cc | |||
| 76d7102e72 | |||
| 74928d9f7a | |||
| a6339587d4 | |||
| 48d28def47 | |||
| e6199da5d6 | |||
| a2f4e9af25 | |||
| b26c64d0d9 | |||
| 930e5b9b4f | |||
| 7f9717266f | |||
| 92c0e83e73 | |||
| 0eb0f5d773 | |||
| 161ff14189 | |||
| 7bcc02c66d | |||
| 31efd3fdef | |||
| 38976d6bc8 | |||
| d25058fdec | |||
| 2453d639ff | |||
| 14c861ccb4 | |||
| 500cb11235 | |||
| ec04fa1fb6 | |||
| a12c22587d | |||
| f30680c26f | |||
| bed69fbfd3 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1 +1,3 @@
 | 
			
		||||
jstester/node_modules
 | 
			
		||||
encoding/tmp
 | 
			
		||||
pdu/tmp
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	13663852	        90.76 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 9525358	       130.7 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	16205492	        74.61 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8466584	       143.2 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 6701893	       221.0 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 7069999	       162.3 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	8.413s
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	16792826	        71.92 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 9800792	       127.2 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	13066466	        79.52 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8162733	       141.6 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 6148492	       272.3 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 7300572	       170.5 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	10.059s
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	  433611	      2688 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 1467840	       683.2 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	  497097	      2389 ns/op
 | 
			
		||||
BenchmarkEncode-6                         	 8964351	       148.5 ns/op
 | 
			
		||||
BenchmarkEncodeInto-6                     	371218869	         2.987 ns/op
 | 
			
		||||
BenchmarkDecode-6                         	1000000000	         0.2681 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	7.851s
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	  457300	      2609 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 2050396	       561.0 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	  504205	      2242 ns/op
 | 
			
		||||
BenchmarkEncode-6                         	27733428	        44.06 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	27795820	        42.97 ns/op
 | 
			
		||||
BenchmarkEncodeInto-6                     	397897166	         3.004 ns/op
 | 
			
		||||
BenchmarkDecode-6                         	1000000000	         0.2653 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	10.935s
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
?   	smpptester	[no test files]
 | 
			
		||||
?   	smpptester/benchmark	[no test files]
 | 
			
		||||
?   	smpptester/client	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/encoding
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkGSM7EncodeSimpleASCIIString-6              	19589385	        67.23 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplexASCIIString-6             	 3757233	       288.3 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIString-6           	 2576941	       532.0 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIStringPrealloc-6   	 2611428	       460.3 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/encoding	6.483s
 | 
			
		||||
?   	smpptester/lua	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	16908814	        71.62 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 8895826	       125.1 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	13243089	        80.48 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8433070	       139.2 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 7134577	       248.2 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 6587127	       166.0 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	8.296s
 | 
			
		||||
?   	smpptester/server	[no test files]
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
?   	smpptester	[no test files]
 | 
			
		||||
?   	smpptester/benchmark	[no test files]
 | 
			
		||||
?   	smpptester/client	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/encoding
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkGSM7EncodeSimpleASCIIString-6              	14935776	        76.91 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplexASCIIString-6             	 3364554	       353.1 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIString-6           	 1840044	       591.6 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIStringPrealloc-6   	 2081725	       520.4 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/encoding	6.270s
 | 
			
		||||
?   	smpptester/lua	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	16804492	        75.91 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	11214482	       134.8 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	15180892	        71.99 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8587657	       139.1 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 5282655	       223.4 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 6659464	       170.4 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	10.264s
 | 
			
		||||
?   	smpptester/server	[no test files]
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
?   	smpptester	[no test files]
 | 
			
		||||
?   	smpptester/benchmark	[no test files]
 | 
			
		||||
?   	smpptester/client	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/encoding
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkGSM7EncodeSimpleASCIIString-6              	17371128	        63.88 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplexASCIIString-6             	 4210346	       332.9 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIString-6           	 2748054	       517.4 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIStringPrealloc-6   	 2576667	       469.9 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/encoding	6.462s
 | 
			
		||||
?   	smpptester/lua	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	16765892	        70.02 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 9238833	       119.7 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	15343066	        75.22 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8445075	       139.0 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 5464392	       274.3 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 6709528	       165.3 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	8.739s
 | 
			
		||||
?   	smpptester/server	[no test files]
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
?   	smpptester	[no test files]
 | 
			
		||||
?   	smpptester/benchmark	[no test files]
 | 
			
		||||
?   	smpptester/client	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/encoding
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkGSM7EncodeSimpleASCIIString-6              	18785672	        65.00 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplexASCIIString-6             	 4313766	       332.7 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIString-6           	 2686035	       520.9 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIStringPrealloc-6   	 2613550	       461.0 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/encoding	6.548s
 | 
			
		||||
?   	smpptester/lua	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	16675815	        69.97 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 9622903	       120.8 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	15080410	        84.43 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8589709	       138.9 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 6965332	       228.4 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 7003172	       165.4 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	8.384s
 | 
			
		||||
?   	smpptester/server	[no test files]
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	15687109	        91.05 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	11083287	       128.9 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	16576201	        74.12 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 7868800	       138.1 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 6833810	       221.2 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 7195328	       166.0 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	8.763s
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	14983336	        90.65 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	10671728	       124.4 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	16471220	        75.06 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 8545360	       139.1 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 6794032	       224.7 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 6737445	       165.3 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	8.681s
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
?   	smpptester	[no test files]
 | 
			
		||||
?   	smpptester/benchmark	[no test files]
 | 
			
		||||
?   	smpptester/client	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/encoding
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkGSM7EncodeSimpleASCIIString-6              	17190692	        63.88 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplexASCIIString-6             	 3987712	       302.8 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIString-6           	 2563090	       523.2 ns/op
 | 
			
		||||
BenchmarkGSM7EncodeComplex8nASCIIStringPrealloc-6   	 2615463	       462.5 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/encoding	6.235s
 | 
			
		||||
?   	smpptester/lua	[no test files]
 | 
			
		||||
goos: linux
 | 
			
		||||
goarch: amd64
 | 
			
		||||
pkg: smpptester/pdu
 | 
			
		||||
cpu: Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
 | 
			
		||||
BenchmarkBufferPoolManager-6              	16913668	        70.22 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Concurrent-6   	 9289608	       129.8 ns/op
 | 
			
		||||
BenchmarkBufferPoolManager_Memory-6       	13000988	        83.78 ns/op
 | 
			
		||||
BenchmarkEncodeWithBufferPool-6           	 7950153	       137.7 ns/op
 | 
			
		||||
BenchmarkEncodeWithoutBufferPool-6        	 6290456	       172.9 ns/op
 | 
			
		||||
BenchmarkDecodeBufferPool-6               	 7194349	       162.7 ns/op
 | 
			
		||||
PASS
 | 
			
		||||
ok  	smpptester/pdu	7.717s
 | 
			
		||||
?   	smpptester/server	[no test files]
 | 
			
		||||
?   	smpptester/utils	[no test files]
 | 
			
		||||
							
								
								
									
										51
									
								
								encoding/.air.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								encoding/.air.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
root = "."
 | 
			
		||||
testdata_dir = "testdata"
 | 
			
		||||
tmp_dir = "tmp"
 | 
			
		||||
 | 
			
		||||
[build]
 | 
			
		||||
  args_bin = []
 | 
			
		||||
  bin = "tmp\\main.exe"
 | 
			
		||||
  cmd = "go test ."
 | 
			
		||||
  delay = 1000
 | 
			
		||||
  exclude_dir = ["assets", "tmp", "vendor", "testdata"]
 | 
			
		||||
  exclude_file = []
 | 
			
		||||
  exclude_regex = []
 | 
			
		||||
  exclude_unchanged = false
 | 
			
		||||
  follow_symlink = false
 | 
			
		||||
  full_bin = ""
 | 
			
		||||
  include_dir = []
 | 
			
		||||
  include_ext = ["go", "tpl", "tmpl", "html"]
 | 
			
		||||
  include_file = []
 | 
			
		||||
  kill_delay = "0s"
 | 
			
		||||
  log = "build-errors.log"
 | 
			
		||||
  poll = false
 | 
			
		||||
  poll_interval = 0
 | 
			
		||||
  post_cmd = []
 | 
			
		||||
  pre_cmd = []
 | 
			
		||||
  rerun = false
 | 
			
		||||
  rerun_delay = 100
 | 
			
		||||
  send_interrupt = false
 | 
			
		||||
  stop_on_error = false
 | 
			
		||||
 | 
			
		||||
[color]
 | 
			
		||||
  app = ""
 | 
			
		||||
  build = "yellow"
 | 
			
		||||
  main = "magenta"
 | 
			
		||||
  runner = "green"
 | 
			
		||||
  watcher = "cyan"
 | 
			
		||||
 | 
			
		||||
[log]
 | 
			
		||||
  main_only = false
 | 
			
		||||
  time = false
 | 
			
		||||
 | 
			
		||||
[misc]
 | 
			
		||||
  clean_on_exit = true
 | 
			
		||||
 | 
			
		||||
[proxy]
 | 
			
		||||
  app_port = 0
 | 
			
		||||
  enabled = false
 | 
			
		||||
  proxy_port = 0
 | 
			
		||||
 | 
			
		||||
[screen]
 | 
			
		||||
  clear_on_rebuild = true
 | 
			
		||||
  keep_scroll = true
 | 
			
		||||
							
								
								
									
										22
									
								
								encoding/ascii.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								encoding/ascii.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import "bytes"
 | 
			
		||||
 | 
			
		||||
type ASCIICoder struct{}
 | 
			
		||||
 | 
			
		||||
func (c *ASCIICoder) Encode(s *string, buf *bytes.Buffer) error {
 | 
			
		||||
	// These should be ASCII but UTF8 is a superset of ASCII so hopefully this'll be fine
 | 
			
		||||
	buf.WriteString(*s)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *ASCIICoder) Decode(buf *bytes.Buffer) (string, error) {
 | 
			
		||||
	return buf.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c ASCIICoder) EncodesInto(s *string) int {
 | 
			
		||||
	return len(*s)
 | 
			
		||||
}
 | 
			
		||||
func (c ASCIICoder) DecodesInto(buf *bytes.Buffer) int {
 | 
			
		||||
	return buf.Len()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										74
									
								
								encoding/ascii_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								encoding/ascii_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestASCIIEncodeSimpleASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &ASCIICoder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := "Hello, World!"
 | 
			
		||||
 | 
			
		||||
	expected := []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Errorf("Expected %v, but got %v", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestASCIIDecodeSimpleASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &ASCIICoder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
 | 
			
		||||
 | 
			
		||||
	expected := "Hello, World!"
 | 
			
		||||
	buf.Write(input)
 | 
			
		||||
	output, err := coder.Decode(&buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if output != expected {
 | 
			
		||||
		t.Errorf("Expected %v, but got %v", expected, output)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestASCIIEncodeEmptyString(t *testing.T) {
 | 
			
		||||
	coder := &ASCIICoder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := ""
 | 
			
		||||
 | 
			
		||||
	expected := []byte{}
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Errorf("Expected %v, but got %v", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestASCIIDecodeEmptyString(t *testing.T) {
 | 
			
		||||
	coder := &ASCIICoder{}
 | 
			
		||||
	buf := bytes.NewBuffer([]byte{})
 | 
			
		||||
 | 
			
		||||
	expected := ""
 | 
			
		||||
	output, err := coder.Decode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if output != expected {
 | 
			
		||||
		t.Errorf("Expected %v, but got %v", expected, output)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								encoding/charset.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								encoding/charset.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
	"ucs2": {
 | 
			
		||||
		"link": "http://www.columbia.edu/kermit/ucs2.html"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								encoding/coder.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								encoding/coder.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import "bytes"
 | 
			
		||||
 | 
			
		||||
type Coder interface {
 | 
			
		||||
	Encode(s *string, buf *bytes.Buffer) error
 | 
			
		||||
	Decode(buf *bytes.Buffer) (string, error)
 | 
			
		||||
	EncodesInto(s *string) int
 | 
			
		||||
	DecodesInto(buf *bytes.Buffer) int
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										136
									
								
								encoding/gsm7.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								encoding/gsm7.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type GSM7Coder struct{}
 | 
			
		||||
 | 
			
		||||
// Make sure buffer can fit EncodesInto bytes
 | 
			
		||||
// Otherwise Encode will allocate memory as it sees fit
 | 
			
		||||
// Which is fine but not optimal
 | 
			
		||||
// Preallocate the buffer with the size of EncodesInto bytes
 | 
			
		||||
func (c *GSM7Coder) Encode(s *string, buf *bytes.Buffer) error {
 | 
			
		||||
	// utf8 := *(*[]byte)(unsafe.Pointer(&s))
 | 
			
		||||
	utf8 := []byte(*s)
 | 
			
		||||
	var (
 | 
			
		||||
		offset      int  = 1
 | 
			
		||||
		bitshift    byte = 0
 | 
			
		||||
		leap, shift bool
 | 
			
		||||
	)
 | 
			
		||||
	encodedSize := c.EncodesInto(s)
 | 
			
		||||
	cap := buf.Cap()
 | 
			
		||||
	if cap < encodedSize {
 | 
			
		||||
		buf.Grow(encodedSize - cap)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for index, septet := range utf8 {
 | 
			
		||||
		if septet > 0b01111111 {
 | 
			
		||||
			return fmt.Errorf("invalid character at index %d", index)
 | 
			
		||||
		}
 | 
			
		||||
		if index == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		bitshift++
 | 
			
		||||
		// log.Printf("Index:%-3d Offset:%-3d Bitshift:%-3d CurrentByte:%08b (%-3d) OffsetByte:%08b (%-3d) Leap:%5v", index, offset, bitshift, utf8[index], utf8[index], utf8[index-offset], utf8[index-offset], leap)
 | 
			
		||||
		mask := byte(255 >> (8 - bitshift))
 | 
			
		||||
		masked := (mask & septet) << (8 - bitshift)
 | 
			
		||||
		// log.Printf("Index:%-3d Offset:%-3d Bitshift:%-3d Mask:%08b Masked:%08b", index, offset, bitshift, mask, masked)
 | 
			
		||||
		if leap {
 | 
			
		||||
			masked >>= 1
 | 
			
		||||
		}
 | 
			
		||||
		utf8[index-offset] |= masked
 | 
			
		||||
		utf8[index] >>= bitshift
 | 
			
		||||
 | 
			
		||||
		if !leap {
 | 
			
		||||
			buf.WriteByte(utf8[index-offset])
 | 
			
		||||
		}
 | 
			
		||||
		if index == len(utf8)-1 && utf8[index] > 0 {
 | 
			
		||||
			buf.WriteByte(utf8[index])
 | 
			
		||||
		}
 | 
			
		||||
		// log.Printf("Index:%-3d Offset:%-3d Bitshift:%-3d CurrentByte:%08b (%-3d) OffsetByte:%08b (%-3d) Leap:%5v", index, offset, bitshift, utf8[index], utf8[index], utf8[index-offset], utf8[index-offset], leap)
 | 
			
		||||
		if bitshift >= 7 {
 | 
			
		||||
			if leap {
 | 
			
		||||
				// log.Printf("Shift at Index:%-3d Offset:%-3d Bitshift:%-3d", index, offset, bitshift)
 | 
			
		||||
				leap = false
 | 
			
		||||
				bitshift = 0
 | 
			
		||||
				offset++
 | 
			
		||||
				shift = true
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			// log.Printf("Leap at Index:%-3d Offset:%-3d Bitshift:%-3d", index, offset, bitshift)
 | 
			
		||||
			leap = true
 | 
			
		||||
			bitshift = 6
 | 
			
		||||
		}
 | 
			
		||||
		if shift {
 | 
			
		||||
			offset = 1
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *GSM7Coder) Decode(buf *bytes.Buffer) (string, error) {
 | 
			
		||||
	gsm7 := buf.Bytes()
 | 
			
		||||
	var (
 | 
			
		||||
		offset   int
 | 
			
		||||
		bitshift byte = 0
 | 
			
		||||
		leap     bool
 | 
			
		||||
	)
 | 
			
		||||
	outLength := c.DecodesInto(buf)
 | 
			
		||||
	lengthDiff := outLength - len(gsm7)
 | 
			
		||||
	gsm7 = append(gsm7, make([]byte, lengthDiff)...)
 | 
			
		||||
	start := len(gsm7) - 2
 | 
			
		||||
 | 
			
		||||
	// We don't care about the last byte
 | 
			
		||||
	// Unless it's the %8.......
 | 
			
		||||
	// We'll deal with that later
 | 
			
		||||
	for index := start; index >= 0; index-- {
 | 
			
		||||
		octet := gsm7[index]
 | 
			
		||||
		bitshift = byte((index % 7) + 1)
 | 
			
		||||
		if bitshift == 7 {
 | 
			
		||||
			leap = true
 | 
			
		||||
		}
 | 
			
		||||
		offset = 1
 | 
			
		||||
		// log.Println(offset, index, index+offset)
 | 
			
		||||
		// log.Printf("Index:%-3d Offset:%-3d Bitshift:%-3d CurrentByte:%08b (%-3d) OffsetByte(%-3d):%08b (%-3d) Leap:%5v", index, offset, bitshift, gsm7[index], gsm7[index], index+offset, gsm7[index+offset], gsm7[index+offset], leap)
 | 
			
		||||
 | 
			
		||||
		mask := byte(255 << (8 - bitshift))
 | 
			
		||||
		masked := (mask & octet) >> (8 - bitshift)
 | 
			
		||||
		// log.Printf("Index:%-3d Offset:%-3d Bitshift:%-3d Mask:%08b Masked:%08b", index, offset, bitshift, mask, masked)
 | 
			
		||||
		if leap {
 | 
			
		||||
			InsertAt(&gsm7, index+offset, masked)
 | 
			
		||||
		} else {
 | 
			
		||||
			gsm7[index+offset] |= masked
 | 
			
		||||
		}
 | 
			
		||||
		// Remove last bitshift bits
 | 
			
		||||
		gsm7[index] <<= bitshift
 | 
			
		||||
		// Move the remaining bit once to the right to form septet instead of octet
 | 
			
		||||
		gsm7[index] >>= 1
 | 
			
		||||
 | 
			
		||||
		// log.Printf("Index:%-3d Offset:%-3d Bitshift:%-3d CurrentByte:%08b (%-3d) OffsetByte(%-3d):%08b (%-3d) Leap:%5v", index, offset, bitshift, gsm7[index], gsm7[index], index+offset, gsm7[index+offset], gsm7[index+offset], leap)
 | 
			
		||||
		leap = false
 | 
			
		||||
	}
 | 
			
		||||
	return string(gsm7), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Allocation free
 | 
			
		||||
// Which means data MUST have space for value
 | 
			
		||||
func InsertAt(data *[]byte, index int, value byte) {
 | 
			
		||||
	copy((*data)[index+1:], (*data)[index:])
 | 
			
		||||
	(*data)[index] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c GSM7Coder) EncodesInto(s *string) int {
 | 
			
		||||
	slen := len(*s)
 | 
			
		||||
	enclen := slen * 7 / 8
 | 
			
		||||
	if slen%8 != 0 {
 | 
			
		||||
		enclen++
 | 
			
		||||
	}
 | 
			
		||||
	return enclen
 | 
			
		||||
}
 | 
			
		||||
func (c GSM7Coder) DecodesInto(buf *bytes.Buffer) int {
 | 
			
		||||
	blen := buf.Len()
 | 
			
		||||
	declen := blen * 8 / 7
 | 
			
		||||
	return declen
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										369
									
								
								encoding/gsm7_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										369
									
								
								encoding/gsm7_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,369 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	short8nString   = "Sunshine"
 | 
			
		||||
	longNot8nString = "Golden rays play, Chasing night away."
 | 
			
		||||
	long8nString    = "Ducks are fucking great, they quacks, O quackers, what the fuck."
 | 
			
		||||
 | 
			
		||||
	short8nStringEncodedBytes   = []byte{0b11010011, 0b10111010, 0b01111011, 0b10001110, 0b01001110, 0b10111011, 0b11001011}
 | 
			
		||||
	longNot8nStringEncodedBytes = []byte{0b11000111, 0b00110111, 0b10011011, 0b01011100, 0b01110110, 0b10000011, 0b11100100, 0b11100001, 0b11111100, 0b00011100, 0b00000100, 0b01100111, 0b10000111, 0b11110011, 0b00101100, 0b11010000, 0b00010000, 0b00011101, 0b10011110, 0b10100111, 0b11011101, 0b01100111, 0b10010000, 0b00111011, 0b01111101, 0b01000110, 0b11010011, 0b01000001, 0b11100001, 0b01111011, 0b00111000, 0b11101111, 0b00000010}
 | 
			
		||||
	long8nStringEncodedBytes    = []byte{0b11000100, 0b11111010, 0b01111000, 0b00111101, 0b00000111, 0b10000101, 0b11100101, 0b01100101, 0b10010000, 0b10111001, 0b00111110, 0b01011110, 0b10100111, 0b11011101, 0b01100111, 0b11010000, 0b01011001, 0b01011110, 0b00001110, 0b11010011, 0b01011001, 0b00100000, 0b00111010, 0b10111010, 0b10011100, 0b00000111, 0b11000101, 0b11101011, 0b11100001, 0b11110001, 0b01111010, 0b11001110, 0b00000010, 0b00111101, 0b01000001, 0b11110001, 0b01111010, 0b01111000, 0b10111100, 0b00101110, 0b11001011, 0b11100111, 0b00101100, 0b11010000, 0b00011101, 0b00011101, 0b10100110, 0b10000011, 0b11101000, 0b11101000, 0b00110010, 0b11001000, 0b01011100, 0b00011111, 0b10101111, 0b01011101}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// region encode
 | 
			
		||||
func TestGSM7EncodeSimpleASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := short8nString
 | 
			
		||||
 | 
			
		||||
	expected := short8nStringEncodedBytes
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodeComplexASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := longNot8nString
 | 
			
		||||
 | 
			
		||||
	expected := longNot8nStringEncodedBytes
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodeComplex8nASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := "Ducks are fucking great, they quacks, O quackers, what the fuck."
 | 
			
		||||
 | 
			
		||||
	expected := long8nStringEncodedBytes
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodeDoesNotAllocateMoreThanNecessary(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	input := "Ducks are fucking great, they quacks, O quackers, what the fuck."
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, coder.EncodesInto(&input)))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
 | 
			
		||||
	expected := buf.Cap()
 | 
			
		||||
	err := coder.Encode(&input, buf)
 | 
			
		||||
	actual := buf.Cap()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected buffer of size %v, but got %v", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodeDoesAllocateWhenNecessary(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	input := "Ducks are fucking great, they quacks, O quackers, what the fuck."
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
 | 
			
		||||
	original := buf.Cap()
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
	modified := buf.Cap()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !(modified > original) {
 | 
			
		||||
		t.Errorf("Expected buffer to grow but buffer changed from %v to %v", original, modified)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodeEmptyString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := ""
 | 
			
		||||
 | 
			
		||||
	expected := []byte{}
 | 
			
		||||
	err := coder.Encode(&input, &buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region decode
 | 
			
		||||
func TestGSM7DecodeSimpleASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := short8nStringEncodedBytes
 | 
			
		||||
 | 
			
		||||
	expected := short8nString
 | 
			
		||||
	buf.Write(input)
 | 
			
		||||
	actual, err := coder.Decode(&buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7DecodeComplexASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := longNot8nStringEncodedBytes
 | 
			
		||||
 | 
			
		||||
	expected := longNot8nString
 | 
			
		||||
	buf.Write(input)
 | 
			
		||||
	actual, err := coder.Decode(&buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7DecodeComplex8nASCIIString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := long8nStringEncodedBytes
 | 
			
		||||
 | 
			
		||||
	expected := long8nString
 | 
			
		||||
	buf.Write(input)
 | 
			
		||||
	actual, err := coder.Decode(&buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7DecodeEmptyString(t *testing.T) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	buf := bytes.NewBuffer([]byte{})
 | 
			
		||||
 | 
			
		||||
	expected := ""
 | 
			
		||||
	actual, err := coder.Decode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, but got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected '%v', but got '%v'", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region insertat
 | 
			
		||||
func TestInsertAtBeginning(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4, 0}
 | 
			
		||||
	InsertAt(&data, 0, 1)
 | 
			
		||||
	expected := []byte{1, 2, 3, 4}
 | 
			
		||||
	if !bytes.Equal(data, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInsertInMiddle(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4, 0}
 | 
			
		||||
	InsertAt(&data, 1, 5)
 | 
			
		||||
	expected := []byte{2, 5, 3, 4}
 | 
			
		||||
	if !bytes.Equal(data, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestInsertAtEnd(t *testing.T) {
 | 
			
		||||
	data := []byte{1, 2, 3, 0}
 | 
			
		||||
	InsertAt(&data, 3, 4)
 | 
			
		||||
	expected := []byte{1, 2, 3, 4}
 | 
			
		||||
	if !bytes.Equal(data, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIndexOutOfBounds(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r == nil {
 | 
			
		||||
			t.Errorf("The code did not panic")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	InsertAt(&data, 4, 5)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestNegativeIndex(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r == nil {
 | 
			
		||||
			t.Errorf("The code did not panic")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	InsertAt(&data, -1, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMaintainsOrderAfterInsertion(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4, 0}
 | 
			
		||||
	InsertAt(&data, 1, 1)
 | 
			
		||||
	expected := []byte{2, 1, 3, 4}
 | 
			
		||||
	if !bytes.Equal(data, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMaintainsLength(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4, 0}
 | 
			
		||||
	InsertAt(&data, 1, 1)
 | 
			
		||||
	if len(data) != 4 {
 | 
			
		||||
		t.Errorf("expected length 4, got %d", len(data))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDeletesLastValue(t *testing.T) {
 | 
			
		||||
	data := []byte{2, 3, 4, 5}
 | 
			
		||||
	InsertAt(&data, 0, 1)
 | 
			
		||||
	expected := []byte{1, 2, 3, 4}
 | 
			
		||||
	if !bytes.Equal(data, expected) {
 | 
			
		||||
		t.Errorf("expected %v, got %v", expected, data)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region misc tests
 | 
			
		||||
func TestGSM7EncodesIntoSmallString(t *testing.T) {
 | 
			
		||||
	input := short8nString
 | 
			
		||||
	expected := 7
 | 
			
		||||
	actual := GSM7Coder{}.EncodesInto(&input)
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected %d, but got %d", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodesIntoLargerNot8nString(t *testing.T) {
 | 
			
		||||
	input := longNot8nString
 | 
			
		||||
	expected := 33
 | 
			
		||||
	actual := GSM7Coder{}.EncodesInto(&input)
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected %d, but got %d", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7EncodesIntoLarger8nString(t *testing.T) {
 | 
			
		||||
	input := long8nString
 | 
			
		||||
	expected := 56
 | 
			
		||||
	actual := GSM7Coder{}.EncodesInto(&input)
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected %d, but got %d", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7DecodesIntoSmallString(t *testing.T) {
 | 
			
		||||
	input := short8nStringEncodedBytes
 | 
			
		||||
	expected := 8
 | 
			
		||||
	actual := GSM7Coder{}.DecodesInto(bytes.NewBuffer(input))
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected %d, but got %d", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7DecodesIntoLargerNot8nString(t *testing.T) {
 | 
			
		||||
	input := longNot8nStringEncodedBytes
 | 
			
		||||
	expected := 37
 | 
			
		||||
	actual := GSM7Coder{}.DecodesInto(bytes.NewBuffer(input))
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected %d, but got %d", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGSM7DecodesIntoLarger8nString(t *testing.T) {
 | 
			
		||||
	input := long8nStringEncodedBytes
 | 
			
		||||
	expected := 64
 | 
			
		||||
	actual := GSM7Coder{}.DecodesInto(bytes.NewBuffer(input))
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("Expected %d, but got %d", expected, actual)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region benchmark
 | 
			
		||||
func BenchmarkGSM7EncodeSimpleASCIIString(b *testing.B) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := short8nString
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		coder.Encode(&input, &buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkGSM7EncodeComplexASCIIString(b *testing.B) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := longNot8nString
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		coder.Encode(&input, &buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkGSM7EncodeComplex8nASCIIString(b *testing.B) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	input := long8nString
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		coder.Encode(&input, &buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkGSM7EncodeComplex8nASCIIStringPrealloc(b *testing.B) {
 | 
			
		||||
	coder := &GSM7Coder{}
 | 
			
		||||
	input := long8nString
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		buf := bytes.NewBuffer(make([]byte, coder.EncodesInto(&input)))
 | 
			
		||||
		buf.Reset()
 | 
			
		||||
		coder.Encode(&input, buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								encoding/ucs2.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								encoding/ucs2.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
package encoding
 | 
			
		||||
 | 
			
		||||
import "bytes"
 | 
			
		||||
 | 
			
		||||
type UCS2Coder struct{}
 | 
			
		||||
 | 
			
		||||
func (c *UCS2Coder) Encode(s *string, buf *bytes.Buffer) error {
 | 
			
		||||
	panic("UCS2 not implemented yet")
 | 
			
		||||
}
 | 
			
		||||
func (c *UCS2Coder) Decode(buf *bytes.Buffer) (string, error) {
 | 
			
		||||
	panic("UCS2 not implemented yet")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c UCS2Coder) EncodesInto(s *string) int {
 | 
			
		||||
	return len(*s) * 2
 | 
			
		||||
}
 | 
			
		||||
func (c UCS2Coder) DecodesInto(buf *bytes.Buffer) int {
 | 
			
		||||
	return buf.Len() / 2
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								main.go
									
									
									
									
									
								
							@@ -136,6 +136,7 @@ func main() {
 | 
			
		||||
	// 		go handleConnection(conn)
 | 
			
		||||
	// 	}
 | 
			
		||||
	// }()
 | 
			
		||||
	log.Println(createSubmitSMPDU())
 | 
			
		||||
 | 
			
		||||
	wg := &sync.WaitGroup{}
 | 
			
		||||
	wg.Add(1)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								pdu/.air.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								pdu/.air.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
root = "."
 | 
			
		||||
testdata_dir = "testdata"
 | 
			
		||||
tmp_dir = "tmp"
 | 
			
		||||
 | 
			
		||||
[build]
 | 
			
		||||
  args_bin = []
 | 
			
		||||
  bin = "tmp\\main.exe"
 | 
			
		||||
  cmd = "go test ."
 | 
			
		||||
  delay = 1000
 | 
			
		||||
  exclude_dir = ["assets", "tmp", "vendor", "testdata"]
 | 
			
		||||
  exclude_file = []
 | 
			
		||||
  exclude_regex = []
 | 
			
		||||
  exclude_unchanged = false
 | 
			
		||||
  follow_symlink = false
 | 
			
		||||
  full_bin = ""
 | 
			
		||||
  include_dir = []
 | 
			
		||||
  include_ext = ["go", "tpl", "tmpl", "html"]
 | 
			
		||||
  include_file = []
 | 
			
		||||
  kill_delay = "0s"
 | 
			
		||||
  log = "build-errors.log"
 | 
			
		||||
  poll = false
 | 
			
		||||
  poll_interval = 0
 | 
			
		||||
  post_cmd = []
 | 
			
		||||
  pre_cmd = []
 | 
			
		||||
  rerun = false
 | 
			
		||||
  rerun_delay = 500
 | 
			
		||||
  send_interrupt = false
 | 
			
		||||
  stop_on_error = false
 | 
			
		||||
 | 
			
		||||
[color]
 | 
			
		||||
  app = ""
 | 
			
		||||
  build = "yellow"
 | 
			
		||||
  main = "magenta"
 | 
			
		||||
  runner = "green"
 | 
			
		||||
  watcher = "cyan"
 | 
			
		||||
 | 
			
		||||
[log]
 | 
			
		||||
  main_only = false
 | 
			
		||||
  time = false
 | 
			
		||||
 | 
			
		||||
[misc]
 | 
			
		||||
  clean_on_exit = false
 | 
			
		||||
 | 
			
		||||
[proxy]
 | 
			
		||||
  app_port = 0
 | 
			
		||||
  enabled = false
 | 
			
		||||
  proxy_port = 0
 | 
			
		||||
 | 
			
		||||
[screen]
 | 
			
		||||
  clear_on_rebuild = false
 | 
			
		||||
  keep_scroll = true
 | 
			
		||||
							
								
								
									
										16
									
								
								pdu/bind.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								pdu/bind.go
									
									
									
									
									
								
							@@ -2,19 +2,19 @@ package pdu
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	BIND struct {
 | 
			
		||||
		header            PDU_HEADER
 | 
			
		||||
		header            *PDU_HEADER
 | 
			
		||||
		system_id         string
 | 
			
		||||
		password          string
 | 
			
		||||
		system_type       string
 | 
			
		||||
		interface_version uint8
 | 
			
		||||
		addr_ton          uint8
 | 
			
		||||
		addr_npi          uint8
 | 
			
		||||
		interface_version byte
 | 
			
		||||
		addr_ton          byte
 | 
			
		||||
		addr_npi          byte
 | 
			
		||||
		address_range     string
 | 
			
		||||
	}
 | 
			
		||||
	BIND_RESP struct {
 | 
			
		||||
		header               PDU_HEADER
 | 
			
		||||
		header               *PDU_HEADER
 | 
			
		||||
		system_id            string
 | 
			
		||||
		sc_interface_version uint8
 | 
			
		||||
		sc_interface_version byte
 | 
			
		||||
	}
 | 
			
		||||
	BIND_RECVEIVER struct {
 | 
			
		||||
		BIND
 | 
			
		||||
@@ -36,9 +36,9 @@ type (
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UNBIND struct {
 | 
			
		||||
		PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
	}
 | 
			
		||||
	UNBIND_RESP struct {
 | 
			
		||||
		PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,30 @@
 | 
			
		||||
package pdu
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"log"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BufferPoolManager struct {
 | 
			
		||||
	pools map[uint]*sync.Pool
 | 
			
		||||
	pools map[int]*sync.Pool
 | 
			
		||||
	mu    sync.RWMutex
 | 
			
		||||
	debug bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bpm *BufferPoolManager) logf(format string, args ...interface{}) {
 | 
			
		||||
	if bpm.debug {
 | 
			
		||||
		log.Printf(format, args...)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewBufferPoolManager() *BufferPoolManager {
 | 
			
		||||
	return &BufferPoolManager{
 | 
			
		||||
		pools: make(map[uint]*sync.Pool),
 | 
			
		||||
		pools: make(map[int]*sync.Pool),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bpm *BufferPoolManager) Get(size uint) *[]uint8 {
 | 
			
		||||
func (bpm *BufferPoolManager) Get(size int) *bytes.Buffer {
 | 
			
		||||
	bpm.mu.RLock()
 | 
			
		||||
	pool, exists := bpm.pools[size]
 | 
			
		||||
	bpm.mu.RUnlock()
 | 
			
		||||
@@ -25,10 +34,13 @@ func (bpm *BufferPoolManager) Get(size uint) *[]uint8 {
 | 
			
		||||
		// Double-check if another goroutine added the pool while we were waiting
 | 
			
		||||
		pool, exists = bpm.pools[size]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			bpm.logf("Creating new pool for size %d\n", size)
 | 
			
		||||
			pool = &sync.Pool{
 | 
			
		||||
				New: func() interface{} {
 | 
			
		||||
					buf := make([]uint8, size)
 | 
			
		||||
					return &buf
 | 
			
		||||
					bpm.logf("Creating new buffer of size %d\n", size)
 | 
			
		||||
					buf := bytes.NewBuffer(make([]byte, size))
 | 
			
		||||
					buf.Reset()
 | 
			
		||||
					return buf
 | 
			
		||||
				},
 | 
			
		||||
			}
 | 
			
		||||
			bpm.pools[size] = pool
 | 
			
		||||
@@ -36,23 +48,23 @@ func (bpm *BufferPoolManager) Get(size uint) *[]uint8 {
 | 
			
		||||
		bpm.mu.Unlock()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return pool.Get().(*[]uint8)
 | 
			
		||||
	buf := pool.Get().(*bytes.Buffer)
 | 
			
		||||
	bpm.logf("Returning buffer of size %d: %p\n", buf.Cap(), buf)
 | 
			
		||||
	return buf
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bpm *BufferPoolManager) Put(buf *[]uint8) {
 | 
			
		||||
	size := uint(len(*buf))
 | 
			
		||||
func (bpm *BufferPoolManager) Put(buf *bytes.Buffer) {
 | 
			
		||||
	size := buf.Cap()
 | 
			
		||||
	bpm.mu.RLock()
 | 
			
		||||
	pool, exists := bpm.pools[size]
 | 
			
		||||
	bpm.mu.RUnlock()
 | 
			
		||||
 | 
			
		||||
	if !exists {
 | 
			
		||||
		bpm.logf("Cannot return %p, No pool for size %d\n", buf, size)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Clear buffer
 | 
			
		||||
	for i := range *buf {
 | 
			
		||||
		(*buf)[i] = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	pool.Put(buf)
 | 
			
		||||
	bpm.logf("Returned buffer of size %d: %p\n", size, buf)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,14 +10,14 @@ func TestRetrieveBufferOfRequestedSize(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	size := 1024
 | 
			
		||||
	buffer := bpm.Get(uint(size))
 | 
			
		||||
	buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
	if buffer == nil {
 | 
			
		||||
		t.Fatalf("Expected buffer, got nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*buffer) != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
	if buffer.Cap() != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -25,14 +25,14 @@ func TestRequestBufferSizeZero(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	size := 0
 | 
			
		||||
	buffer := bpm.Get(uint(size))
 | 
			
		||||
	buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
	if buffer == nil {
 | 
			
		||||
		t.Fatalf("Expected buffer, got nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*buffer) != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
	if buffer.Cap() != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -47,12 +47,12 @@ func TestConcurrentAccessToBufferPool(t *testing.T) {
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			buffer := bpm.Get(uint(size))
 | 
			
		||||
			buffer := bpm.Get(size)
 | 
			
		||||
			if buffer == nil {
 | 
			
		||||
				t.Errorf("Expected buffer, got nil")
 | 
			
		||||
			}
 | 
			
		||||
			if len(*buffer) != size {
 | 
			
		||||
				t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
			if buffer.Cap() != size {
 | 
			
		||||
				t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
@@ -64,14 +64,14 @@ func TestGetBufferLockUnlock(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	size := 1024
 | 
			
		||||
	buffer := bpm.Get(uint(size))
 | 
			
		||||
	buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
	if buffer == nil {
 | 
			
		||||
		t.Fatalf("Expected buffer, got nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*buffer) != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
	if buffer.Cap() != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -79,14 +79,14 @@ func TestVerifyPoolCreationForNewSizes(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	size := 512
 | 
			
		||||
	buffer := bpm.Get(uint(size))
 | 
			
		||||
	buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
	if buffer == nil {
 | 
			
		||||
		t.Fatalf("Expected buffer, got nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*buffer) != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
	if buffer.Cap() != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -94,14 +94,14 @@ func TestBufferPoolManagerGetBuffer(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	size := 1024
 | 
			
		||||
	buffer := bpm.Get(uint(size))
 | 
			
		||||
	buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
	if buffer == nil {
 | 
			
		||||
		t.Fatalf("Expected buffer, got nil")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(*buffer) != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
	if buffer.Cap() != size {
 | 
			
		||||
		t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -110,14 +110,14 @@ func TestGetBufferWithMultipleSizes(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	sizes := []int{512, 1024, 2048}
 | 
			
		||||
	for _, size := range sizes {
 | 
			
		||||
		buffer := bpm.Get(uint(size))
 | 
			
		||||
		buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
		if buffer == nil {
 | 
			
		||||
			t.Fatalf("Expected buffer for size %d, got nil", size)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(*buffer) != size {
 | 
			
		||||
			t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
		if buffer.Cap() != size {
 | 
			
		||||
			t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -125,7 +125,7 @@ func TestGetBufferWithMultipleSizes(t *testing.T) {
 | 
			
		||||
func TestGetBufferIsAlwaysZero(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	var size uint = 1024 * 64
 | 
			
		||||
	size := 1024 * 64
 | 
			
		||||
	for i := 0; i < 1000; i++ {
 | 
			
		||||
		buffer := bpm.Get(size)
 | 
			
		||||
 | 
			
		||||
@@ -133,11 +133,11 @@ func TestGetBufferIsAlwaysZero(t *testing.T) {
 | 
			
		||||
			t.Fatalf("Expected buffer for size %d, got nil", size)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if uint(len(*buffer)) != size {
 | 
			
		||||
			t.Errorf("Expected buffer size %d, got %d", size, len(*buffer))
 | 
			
		||||
		if buffer.Cap() != size {
 | 
			
		||||
			t.Errorf("Expected buffer size %d, got %d", size, buffer.Cap())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for _, b := range *buffer {
 | 
			
		||||
		for _, b := range buffer.Bytes() {
 | 
			
		||||
			if b != 0 {
 | 
			
		||||
				t.Errorf("Expected buffer to be zero, got %d", b)
 | 
			
		||||
			}
 | 
			
		||||
@@ -147,9 +147,26 @@ func TestGetBufferIsAlwaysZero(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPoolReusesBuffers(t *testing.T) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
 | 
			
		||||
	size := 1024
 | 
			
		||||
	buffer := bpm.Get(size)
 | 
			
		||||
	initialPtr := buffer
 | 
			
		||||
	bpm.Put(buffer)
 | 
			
		||||
	for i := 0; i < 1000; i++ {
 | 
			
		||||
		buffer = bpm.Get(size)
 | 
			
		||||
		if buffer != initialPtr {
 | 
			
		||||
			t.Errorf("Expected initial buffer (%p) to be reused, got %p", initialPtr, buffer)
 | 
			
		||||
		}
 | 
			
		||||
		bpm.Put(buffer)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region benchmark
 | 
			
		||||
func BenchmarkBufferPoolManager(b *testing.B) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
	bufSize := uint(128 * 1024) // a PDU should not be larger than this... Even this is way too large
 | 
			
		||||
	bufSize := 128 * 1024 // a PDU should not be larger than this... Even this is way too large
 | 
			
		||||
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
@@ -163,7 +180,7 @@ func BenchmarkBufferPoolManager(b *testing.B) {
 | 
			
		||||
 | 
			
		||||
func BenchmarkBufferPoolManager_Concurrent(b *testing.B) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
	bufSize := uint(128 * 1024) // a PDU should not be larger than this... Even this is vway too large
 | 
			
		||||
	bufSize := 128 * 1024 // a PDU should not be larger than this... Even this is way too large
 | 
			
		||||
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
@@ -186,17 +203,17 @@ func BenchmarkBufferPoolManager_Concurrent(b *testing.B) {
 | 
			
		||||
 | 
			
		||||
func BenchmarkBufferPoolManager_Memory(b *testing.B) {
 | 
			
		||||
	bpm := NewBufferPoolManager()
 | 
			
		||||
	bufSize := uint(128 * 1024) // a PDU should not be larger than this... Even this is vway too large
 | 
			
		||||
	bufSize := 128 * 1024 // a PDU should not be larger than this... Even this is way too large
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
 | 
			
		||||
	var i uint8
 | 
			
		||||
	var i byte
 | 
			
		||||
	buf := bpm.Get(bufSize)
 | 
			
		||||
	b.StopTimer()
 | 
			
		||||
 | 
			
		||||
	// Simulate some work
 | 
			
		||||
	time.Sleep(10 * time.Millisecond)
 | 
			
		||||
	for k := range *buf {
 | 
			
		||||
		(*buf)[k] = i % 255
 | 
			
		||||
	for range buf.Bytes() {
 | 
			
		||||
		buf.WriteByte(i % 255)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	b.StartTimer()
 | 
			
		||||
 
 | 
			
		||||
@@ -2,17 +2,17 @@ package pdu
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	CANCEL_SM struct {
 | 
			
		||||
		header           PDU_HEADER
 | 
			
		||||
		header           *PDU_HEADER
 | 
			
		||||
		service_type     string
 | 
			
		||||
		message_id       string
 | 
			
		||||
		source_addr_ton  uint8
 | 
			
		||||
		source_addr_npi  uint8
 | 
			
		||||
		source_addr_ton  byte
 | 
			
		||||
		source_addr_npi  byte
 | 
			
		||||
		source_addr      string
 | 
			
		||||
		dest_addr_ton    uint8
 | 
			
		||||
		dest_addr_npi    uint8
 | 
			
		||||
		dest_addr_ton    byte
 | 
			
		||||
		dest_addr_npi    byte
 | 
			
		||||
		destination_addr string
 | 
			
		||||
	}
 | 
			
		||||
	CANCEL_SM_RESP struct {
 | 
			
		||||
		header PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,11 @@ package pdu
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	DELIVER_SM struct {
 | 
			
		||||
		header PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
		SUBMIT_SM
 | 
			
		||||
	}
 | 
			
		||||
	DELIVER_SM_RESP struct {
 | 
			
		||||
		header PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
		SUBMIT_SM_RESP
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@ package pdu
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	ENQUIRE_LINK struct {
 | 
			
		||||
		header PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
	}
 | 
			
		||||
	ENQUIRE_LINK_RESP struct {
 | 
			
		||||
		header PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
package pdu
 | 
			
		||||
 | 
			
		||||
var ByteBufferPool = NewBufferPoolManager()
 | 
			
		||||
const NULL = byte(0x00)
 | 
			
		||||
var NULL_ARR = []byte{NULL}
 | 
			
		||||
							
								
								
									
										57
									
								
								pdu/pdu.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								pdu/pdu.go
									
									
									
									
									
								
							@@ -1,17 +1,18 @@
 | 
			
		||||
package pdu
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	PDU interface {
 | 
			
		||||
		EncodeInto(*[]uint8)
 | 
			
		||||
		Encode() []uint8
 | 
			
		||||
		Decode([]uint8)
 | 
			
		||||
		Encode(*bytes.Buffer) error
 | 
			
		||||
		Decode(*bytes.Buffer) error
 | 
			
		||||
		// Size in bytes
 | 
			
		||||
		Size() uint32
 | 
			
		||||
		Size() int
 | 
			
		||||
		UpdateSize()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	PDU_HEADER struct {
 | 
			
		||||
@@ -22,7 +23,7 @@ type (
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	GENERIC_NACK struct {
 | 
			
		||||
		header PDU_HEADER
 | 
			
		||||
		header *PDU_HEADER
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -57,41 +58,29 @@ type (
 | 
			
		||||
// What are the other 0s?
 | 
			
		||||
// Don't know
 | 
			
		||||
 | 
			
		||||
func (p *PDU_HEADER) Encode() (*[]uint8, error) {
 | 
			
		||||
	buf := ByteBufferPool.Get(uint(p.Size()))
 | 
			
		||||
	err := p.EncodeInto(buf)
 | 
			
		||||
	return buf, err
 | 
			
		||||
}
 | 
			
		||||
func (p *PDU_HEADER) EncodeInto(buf *[]uint8) error {
 | 
			
		||||
func (p *PDU_HEADER) Encode(buf *bytes.Buffer) error {
 | 
			
		||||
	if buf == nil {
 | 
			
		||||
		return fmt.Errorf("cannot encode PDU_HEADER, buffer is nil")
 | 
			
		||||
		return fmt.Errorf("cannot encode into nil buffer")
 | 
			
		||||
	}
 | 
			
		||||
	if len(*buf) < 16 {
 | 
			
		||||
		return fmt.Errorf("cannot encode PDU_HEADER, buffer too small (%d, required 16)", len(*buf))
 | 
			
		||||
	}
 | 
			
		||||
	bufVal := *buf
 | 
			
		||||
	binary.BigEndian.PutUint32(bufVal[0:4], p.command_length)
 | 
			
		||||
	binary.BigEndian.PutUint32(bufVal[4:8], p.command_id)
 | 
			
		||||
	binary.BigEndian.PutUint32(bufVal[8:12], p.command_status)
 | 
			
		||||
	binary.BigEndian.PutUint32(bufVal[12:16], p.sequence_number)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.command_length)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.command_id)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.command_status)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.sequence_number)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
func (p *PDU_HEADER) Decode(data []uint8) error {
 | 
			
		||||
	if len(data) >= 4 {
 | 
			
		||||
		p.command_length = binary.BigEndian.Uint32(data[0:4])
 | 
			
		||||
	}
 | 
			
		||||
	if len(data) >= 8 {
 | 
			
		||||
		p.command_id = binary.BigEndian.Uint32(data[4:8])
 | 
			
		||||
	}
 | 
			
		||||
	if len(data) >= 12 {
 | 
			
		||||
		p.command_status = binary.BigEndian.Uint32(data[8:12])
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	if len(data) >= 16 {
 | 
			
		||||
		p.sequence_number = binary.BigEndian.Uint32(data[12:16])
 | 
			
		||||
func (p *PDU_HEADER) Decode(buf *bytes.Buffer) error {
 | 
			
		||||
	if buf == nil {
 | 
			
		||||
		return fmt.Errorf("cannot decode nil buffer")
 | 
			
		||||
	}
 | 
			
		||||
	binary.Read(buf, binary.BigEndian, &p.command_length)
 | 
			
		||||
	binary.Read(buf, binary.BigEndian, &p.command_id)
 | 
			
		||||
	binary.Read(buf, binary.BigEndian, &p.command_status)
 | 
			
		||||
	binary.Read(buf, binary.BigEndian, &p.sequence_number)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
func (p *PDU_HEADER) Size() uint32 {
 | 
			
		||||
func (p *PDU_HEADER) Size() int {
 | 
			
		||||
	return 16
 | 
			
		||||
}
 | 
			
		||||
func (p *PDU_HEADER) UpdateSize() {
 | 
			
		||||
	p.command_length = uint32(p.Size())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										258
									
								
								pdu/pdu_test.go
									
									
									
									
									
								
							
							
						
						
									
										258
									
								
								pdu/pdu_test.go
									
									
									
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
package pdu
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"math"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -15,13 +16,15 @@ func TestEncodeReturnsByteSliceOfLength16(t *testing.T) {
 | 
			
		||||
		command_status:  1,
 | 
			
		||||
		sequence_number: 1,
 | 
			
		||||
	}
 | 
			
		||||
	result, err := p.Encode()
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(*result) != 16 {
 | 
			
		||||
		t.Errorf("Expected byte slice of length 16, got %d", len(*result))
 | 
			
		||||
	if buf.Cap() != 16 {
 | 
			
		||||
		t.Errorf("Expected byte slice of length 16, got %d", buf.Cap())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -32,15 +35,17 @@ func TestEncodeHandlesZeroValues(t *testing.T) {
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 0,
 | 
			
		||||
	}
 | 
			
		||||
	result, err := p.Encode()
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	expected := make([]uint8, 16)
 | 
			
		||||
	for i, v := range *result {
 | 
			
		||||
	expected := make([]byte, 16)
 | 
			
		||||
	for i, v := range buf.Bytes() {
 | 
			
		||||
		if v != expected[i] {
 | 
			
		||||
			t.Errorf("Expected byte slice with zero values, got %v", *result)
 | 
			
		||||
			t.Errorf("Expected byte slice with zero values, got %v", buf.Bytes())
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -53,16 +58,18 @@ func TestEncodeEncodesProperly(t *testing.T) {
 | 
			
		||||
		command_status:  3,
 | 
			
		||||
		sequence_number: 4,
 | 
			
		||||
	}
 | 
			
		||||
	result, err := p.Encode()
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []uint8{0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4}
 | 
			
		||||
	for i, v := range *result {
 | 
			
		||||
	expected := []byte{0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4}
 | 
			
		||||
	for i, v := range buf.Bytes() {
 | 
			
		||||
		if v != expected[i] {
 | 
			
		||||
			t.Errorf("Expected byte slice with values %v, got %v", expected, *result)
 | 
			
		||||
			t.Errorf("Expected byte slice with values %v, got %v", expected, buf.Bytes())
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -75,16 +82,18 @@ func TestEncodeEncodesProperlyComplex(t *testing.T) {
 | 
			
		||||
		command_status:  35634264,
 | 
			
		||||
		sequence_number: 476543523,
 | 
			
		||||
	}
 | 
			
		||||
	result, err := p.Encode()
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []uint8{0, 204, 224, 36, 0, 3, 225, 144, 2, 31, 188, 88, 28, 103, 122, 35}
 | 
			
		||||
	for i, v := range *result {
 | 
			
		||||
	expected := []byte{0, 204, 224, 36, 0, 3, 225, 144, 2, 31, 188, 88, 28, 103, 122, 35}
 | 
			
		||||
	for i, v := range buf.Bytes() {
 | 
			
		||||
		if v != expected[i] {
 | 
			
		||||
			t.Errorf("Expected byte slice with values %v, got %v", expected, *result)
 | 
			
		||||
			t.Errorf("Expected byte slice with values %v, got %v", expected, buf.Bytes())
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -97,54 +106,26 @@ func TestEncodeIntoCorrectlyEncodesFields(t *testing.T) {
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 12345,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 16)
 | 
			
		||||
	err := p.EncodeInto(&buf)
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4]))
 | 
			
		||||
	innerbuf := buf.Bytes()
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEncodeIntoHandlesNilBuffer(t *testing.T) {
 | 
			
		||||
	p := &PDU_HEADER{
 | 
			
		||||
		command_length:  16,
 | 
			
		||||
		command_id:      1,
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 12345,
 | 
			
		||||
	}
 | 
			
		||||
	var buf *[]uint8 = nil
 | 
			
		||||
 | 
			
		||||
	err := p.EncodeInto(buf)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("Expected error when buffer is nil")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestEncodeIntoHandlesSmallerBuffer(t *testing.T) {
 | 
			
		||||
	p := &PDU_HEADER{
 | 
			
		||||
		command_length:  16,
 | 
			
		||||
		command_id:      1,
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 12345,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 12) // smaller buffer size
 | 
			
		||||
 | 
			
		||||
	err := p.EncodeInto(&buf)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("Expected error when buffer is too small")
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -155,24 +136,26 @@ func TestEncodeIntoHandlesLargerBuffer(t *testing.T) {
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 12345,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 20)
 | 
			
		||||
	err := p.EncodeInto(&buf)
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 20)) // larger buffer size
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4]))
 | 
			
		||||
	innerbuf := buf.Bytes()
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -183,24 +166,26 @@ func TestEncodeIntoUsesBigEndianEncoding(t *testing.T) {
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 12345,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 16)
 | 
			
		||||
	err := p.EncodeInto(&buf)
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4]))
 | 
			
		||||
	innerbuf := buf.Bytes()
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -211,28 +196,30 @@ func TestEncodeIntoConcurrencySafety(t *testing.T) {
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 12345,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 16)
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	for i := 0; i < 1000; i++ {
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			p.EncodeInto(&buf)
 | 
			
		||||
			p.Encode(buf)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4]))
 | 
			
		||||
	innerbuf := buf.Bytes()
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -243,24 +230,26 @@ func TestEncodeIntoWithMaximumValues(t *testing.T) {
 | 
			
		||||
		command_status:  math.MaxUint32,
 | 
			
		||||
		sequence_number: math.MaxUint32,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 16)
 | 
			
		||||
	err := p.EncodeInto(&buf)
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4]))
 | 
			
		||||
	innerbuf := buf.Bytes()
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -271,38 +260,39 @@ func TestEncodeIntoWithBoundaryValues(t *testing.T) {
 | 
			
		||||
		command_status:  0,
 | 
			
		||||
		sequence_number: 0,
 | 
			
		||||
	}
 | 
			
		||||
	buf := make([]uint8, 16)
 | 
			
		||||
	err := p.EncodeInto(&buf)
 | 
			
		||||
	buf := bytes.NewBuffer(make([]byte, 16))
 | 
			
		||||
	buf.Reset()
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(buf[0:4]))
 | 
			
		||||
	innerbuf := buf.Bytes()
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[0:4]) != p.command_length {
 | 
			
		||||
		t.Errorf("Expected command_length %d, got %d", p.command_length, binary.BigEndian.Uint32(innerbuf[0:4]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(buf[4:8]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[4:8]) != p.command_id {
 | 
			
		||||
		t.Errorf("Expected command_id %d, got %d", p.command_id, binary.BigEndian.Uint32(innerbuf[4:8]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(buf[8:12]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[8:12]) != p.command_status {
 | 
			
		||||
		t.Errorf("Expected command_status %d, got %d", p.command_status, binary.BigEndian.Uint32(innerbuf[8:12]))
 | 
			
		||||
	}
 | 
			
		||||
	if binary.BigEndian.Uint32(buf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(buf[12:16]))
 | 
			
		||||
	if binary.BigEndian.Uint32(innerbuf[12:16]) != p.sequence_number {
 | 
			
		||||
		t.Errorf("Expected sequence_number %d, got %d", p.sequence_number, binary.BigEndian.Uint32(innerbuf[12:16]))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// region decode
 | 
			
		||||
func TestDecodeHandlesShortByteSlice(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := []uint8{0, 0, 0, 10}
 | 
			
		||||
	data := []byte{0, 0, 0, 10}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			t.Errorf("Decode panicked with short byte slice")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -310,8 +300,8 @@ func TestDecodeHandlesShortByteSlice(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestDecodeParsesValidByteSlice(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := []uint8{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
@@ -333,13 +323,13 @@ func TestDecodeParsesValidByteSlice(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestDecodeHandlesLongerByteSliceWithoutCrashing(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := make([]uint8, 20)
 | 
			
		||||
	data := make([]byte, 20)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if r := recover(); r != nil {
 | 
			
		||||
			t.Errorf("Decode panicked with long byte slice")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -349,8 +339,8 @@ func TestDecodeHandlesNilDataInput(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	err := p.Decode(nil)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Errorf("Expected error, got none")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.command_length != 0 {
 | 
			
		||||
@@ -369,8 +359,8 @@ func TestDecodeHandlesNilDataInput(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestDecodeHandlesEmptyByteSliceGracefully(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := []uint8{}
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	data := []byte{}
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
@@ -392,11 +382,11 @@ func TestDecodeHandlesEmptyByteSliceGracefully(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestDecodeDoesNotModifyInputByteSlice(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := []uint8{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
 | 
			
		||||
	originalData := make([]uint8, len(data))
 | 
			
		||||
	data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
 | 
			
		||||
	originalData := make([]byte, len(data))
 | 
			
		||||
	copy(originalData, data)
 | 
			
		||||
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
@@ -411,8 +401,8 @@ func TestDecodeDoesNotModifyInputByteSlice(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestDecodeHandlesByteSlicesWithMaxUint32Values(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := []uint8{255, 255, 255, 255, 255, 255, 255, 255}
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	data := []byte{255, 255, 255, 255, 255, 255, 255, 255}
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
@@ -428,8 +418,8 @@ func TestDecodeHandlesByteSlicesWithMaxUint32Values(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestDecodeHandlesByteSlicesWithMinimumUint32Values(t *testing.T) {
 | 
			
		||||
	var p PDU_HEADER
 | 
			
		||||
	data := []uint8{0, 0, 0, 0, 0, 0, 0, 0}
 | 
			
		||||
	err := p.Decode(data)
 | 
			
		||||
	data := []byte{0, 0, 0, 0, 0, 0, 0, 0}
 | 
			
		||||
	err := p.Decode(bytes.NewBuffer(data))
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
@@ -452,9 +442,8 @@ func TestSizeReturns16(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region benchmarks
 | 
			
		||||
 | 
			
		||||
// With buffer pool
 | 
			
		||||
func BenchmarkEncode(b *testing.B) {
 | 
			
		||||
func BenchmarkEncodeWithBufferPool(b *testing.B) {
 | 
			
		||||
	p := &PDU_HEADER{
 | 
			
		||||
		command_length:  16,
 | 
			
		||||
		command_id:      1,
 | 
			
		||||
@@ -463,12 +452,14 @@ func BenchmarkEncode(b *testing.B) {
 | 
			
		||||
	}
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		p.Encode()
 | 
			
		||||
		buf := ByteBufferPool.Get(p.Size())
 | 
			
		||||
		p.Encode(buf)
 | 
			
		||||
		ByteBufferPool.Put(buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Without buffer pool
 | 
			
		||||
func BenchmarkEncodeInto(b *testing.B) {
 | 
			
		||||
func BenchmarkEncodeWithoutBufferPool(b *testing.B) {
 | 
			
		||||
	p := &PDU_HEADER{
 | 
			
		||||
		command_length:  16,
 | 
			
		||||
		command_id:      1,
 | 
			
		||||
@@ -477,16 +468,19 @@ func BenchmarkEncodeInto(b *testing.B) {
 | 
			
		||||
	}
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		buf := make([]uint8, 16)
 | 
			
		||||
		p.EncodeInto(&buf)
 | 
			
		||||
		buf := bytes.Buffer{}
 | 
			
		||||
		p.Encode(&buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func BenchmarkDecode(b *testing.B) {
 | 
			
		||||
func BenchmarkDecodeBufferPool(b *testing.B) {
 | 
			
		||||
	p := &PDU_HEADER{}
 | 
			
		||||
	data := []uint8{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
 | 
			
		||||
	b.ResetTimer()
 | 
			
		||||
	for i := 0; i < b.N; i++ {
 | 
			
		||||
		p.Decode(data)
 | 
			
		||||
		data := []byte{0, 0, 0, 16, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3}
 | 
			
		||||
		buf := ByteBufferPool.Get(len(data))
 | 
			
		||||
		buf.Write(data)
 | 
			
		||||
		p.Decode(buf)
 | 
			
		||||
		ByteBufferPool.Put(buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										159
									
								
								pdu/submit.go
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								pdu/submit.go
									
									
									
									
									
								
							@@ -1,104 +1,153 @@
 | 
			
		||||
package pdu
 | 
			
		||||
 | 
			
		||||
import "fmt"
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"smpptester/encoding"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	SUBMIT_SM struct {
 | 
			
		||||
		header                 PDU_HEADER
 | 
			
		||||
		header                 *PDU_HEADER
 | 
			
		||||
		service_type           string
 | 
			
		||||
		source_addr_ton        uint8
 | 
			
		||||
		source_addr_npi        uint8
 | 
			
		||||
		source_addr_ton        byte
 | 
			
		||||
		source_addr_npi        byte
 | 
			
		||||
		source_addr            string
 | 
			
		||||
		dest_addr_ton          uint8
 | 
			
		||||
		dest_addr_npi          uint8
 | 
			
		||||
		dest_addr_ton          byte
 | 
			
		||||
		dest_addr_npi          byte
 | 
			
		||||
		destination_addr       string
 | 
			
		||||
		esm_class              uint8
 | 
			
		||||
		protocol_id            uint8
 | 
			
		||||
		priority_flag          uint8
 | 
			
		||||
		esm_class              byte
 | 
			
		||||
		protocol_id            byte
 | 
			
		||||
		priority_flag          byte
 | 
			
		||||
		schedule_delivery_time string
 | 
			
		||||
		validity_period        string
 | 
			
		||||
		registered_delivery    uint8
 | 
			
		||||
		replace_if_present     uint8
 | 
			
		||||
		data_coding            uint8
 | 
			
		||||
		sm_default_msg_id      uint8
 | 
			
		||||
		sm_length              uint8
 | 
			
		||||
		registered_delivery    byte
 | 
			
		||||
		replace_if_present     byte
 | 
			
		||||
		data_coding            byte
 | 
			
		||||
		sm_default_msg_id      byte
 | 
			
		||||
		sm_length              byte
 | 
			
		||||
		short_message          string
 | 
			
		||||
		// user_message_reference uint16
 | 
			
		||||
		// source_port               uint16
 | 
			
		||||
		// source_addr_subunit       uint8
 | 
			
		||||
		// source_addr_subunit       byte
 | 
			
		||||
		// destination_port          uint16
 | 
			
		||||
		// dest_addr_subunit         uint8
 | 
			
		||||
		// dest_addr_subunit         byte
 | 
			
		||||
		// sar_msg_ref_num           uint16
 | 
			
		||||
		// sar_total_segments        uint8
 | 
			
		||||
		// sar_segment_seqnum        uint8
 | 
			
		||||
		// more_messages_to_send     uint8
 | 
			
		||||
		// payload_type              uint8
 | 
			
		||||
		// sar_total_segments        byte
 | 
			
		||||
		// sar_segment_seqnum        byte
 | 
			
		||||
		// more_messages_to_send     byte
 | 
			
		||||
		// payload_type              byte
 | 
			
		||||
		// message_payload           string
 | 
			
		||||
		// privacy_indicator         uint8
 | 
			
		||||
		// privacy_indicator         byte
 | 
			
		||||
		// callback_num              string
 | 
			
		||||
		// callback_num_pres         uint8
 | 
			
		||||
		// callback_num_pres         byte
 | 
			
		||||
		// callback_num_atag         string
 | 
			
		||||
		// source_subaddress         string
 | 
			
		||||
		// dest_subaddress           string
 | 
			
		||||
		// user_response_code        uint8
 | 
			
		||||
		// display_time              uint8
 | 
			
		||||
		// sms_signal                uint8
 | 
			
		||||
		// ms_validity               uint8
 | 
			
		||||
		// ms_msg_wait_facilities    uint8
 | 
			
		||||
		// number_of_messages        uint8
 | 
			
		||||
		// alert_on_message_delivery uint8
 | 
			
		||||
		// language_indicator        uint8
 | 
			
		||||
		// its_reply_type            uint8
 | 
			
		||||
		// its_session_info          uint8
 | 
			
		||||
		// ussd_service_op           uint8
 | 
			
		||||
		// user_response_code        byte
 | 
			
		||||
		// display_time              byte
 | 
			
		||||
		// sms_signal                byte
 | 
			
		||||
		// ms_validity               byte
 | 
			
		||||
		// ms_msg_wait_facilities    byte
 | 
			
		||||
		// number_of_messages        byte
 | 
			
		||||
		// alert_on_message_delivery byte
 | 
			
		||||
		// language_indicator        byte
 | 
			
		||||
		// its_reply_type            byte
 | 
			
		||||
		// its_session_info          byte
 | 
			
		||||
		// ussd_service_op           byte
 | 
			
		||||
	}
 | 
			
		||||
	SUBMIT_SM_RESP struct {
 | 
			
		||||
		header     PDU_HEADER
 | 
			
		||||
		header     *PDU_HEADER
 | 
			
		||||
		message_id string
 | 
			
		||||
	}
 | 
			
		||||
	SUBMIT_MULTI      struct{}
 | 
			
		||||
	SUBMIT_MULTI_RESP struct{}
 | 
			
		||||
)
 | 
			
		||||
// See https://www.codeproject.com/Tips/470755/Encoding-Decoding-7-bit-User-Data-for-SMS-PDU-PDU
 | 
			
		||||
// Another great site: https://doubleblak.com/blogPost.php?k=7bitpdu
 | 
			
		||||
 | 
			
		||||
func (p *SUBMIT_SM) Encode() (*[]byte, error) {
 | 
			
		||||
	buf := ByteBufferPool.Get(uint(p.Size()))
 | 
			
		||||
	err := p.EncodeInto(buf)
 | 
			
		||||
	return buf, err
 | 
			
		||||
}
 | 
			
		||||
func (p *SUBMIT_SM) EncodeInto(buf *[]byte) error {
 | 
			
		||||
func (p *SUBMIT_SM) Encode(buf *bytes.Buffer) error {
 | 
			
		||||
	if buf == nil {
 | 
			
		||||
		return fmt.Errorf("cannot encode SUBMIT_SM, buffer is nil")
 | 
			
		||||
		return fmt.Errorf("cannot encode into nil buffer")
 | 
			
		||||
	}
 | 
			
		||||
	if len(*buf) < int(p.Size()) {
 | 
			
		||||
		return fmt.Errorf("cannot encode SUBMIT_SM, buffer too small (%d, required %d)", len(*buf), p.Size())
 | 
			
		||||
	messageEncoder := p.GetEncoder()
 | 
			
		||||
 | 
			
		||||
	p.header.Encode(buf)
 | 
			
		||||
	// These should be ASCII but UTF8 is a superset of ASCII so hopefully this'll be fine
 | 
			
		||||
	buf.WriteString(p.service_type)
 | 
			
		||||
	buf.Write(NULL_ARR)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.source_addr_ton)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.source_addr_npi)
 | 
			
		||||
	buf.WriteString(p.source_addr)
 | 
			
		||||
	buf.Write(NULL_ARR)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.dest_addr_ton)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.dest_addr_npi)
 | 
			
		||||
	buf.WriteString(p.destination_addr)
 | 
			
		||||
	buf.Write(NULL_ARR)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.esm_class)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.protocol_id)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.priority_flag)
 | 
			
		||||
	buf.WriteString(p.schedule_delivery_time)
 | 
			
		||||
	buf.Write(NULL_ARR)
 | 
			
		||||
	buf.WriteString(p.validity_period)
 | 
			
		||||
	buf.Write(NULL_ARR)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.registered_delivery)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.replace_if_present)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.data_coding)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.sm_default_msg_id)
 | 
			
		||||
	binary.Write(buf, binary.BigEndian, p.sm_length)
 | 
			
		||||
	err := messageEncoder.Encode(&p.short_message, buf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.header.EncodeInto(buf)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
func (p *SUBMIT_SM) Decode(data []byte) {
 | 
			
		||||
func (p *SUBMIT_SM) Decode(buf *bytes.Buffer) error {
 | 
			
		||||
	if buf == nil {
 | 
			
		||||
		return fmt.Errorf("cannot decode nil buffer")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
func (p *SUBMIT_SM) Size() uint32 {
 | 
			
		||||
	var size uint32
 | 
			
		||||
func (p *SUBMIT_SM) Size() int {
 | 
			
		||||
	var size int
 | 
			
		||||
	size += p.header.Size()
 | 
			
		||||
	size += uint32(len(p.service_type) * 1)
 | 
			
		||||
	size += 1 + len(p.service_type)
 | 
			
		||||
	size += 1 // source_addr_ton
 | 
			
		||||
	size += 1 // source_addr_npi
 | 
			
		||||
	size += uint32(len(p.source_addr) * 1)
 | 
			
		||||
	size += 1 + len(p.source_addr)
 | 
			
		||||
	size += 1 // dest_addr_ton
 | 
			
		||||
	size += 1 // dest_addr_npi
 | 
			
		||||
	size += uint32(len(p.destination_addr) * 1)
 | 
			
		||||
	size += 1 + len(p.destination_addr)
 | 
			
		||||
	size += 1 // esm_class
 | 
			
		||||
	size += 1 // protocol_id
 | 
			
		||||
	size += 1 // priority_flag
 | 
			
		||||
	size += uint32(len(p.schedule_delivery_time) * 1)
 | 
			
		||||
	size += uint32(len(p.validity_period) * 1)
 | 
			
		||||
	size += 1 + len(p.schedule_delivery_time)
 | 
			
		||||
	size += 1 + len(p.validity_period)
 | 
			
		||||
	size += 1 // registered_delivery
 | 
			
		||||
	size += 1 // replace_if_present
 | 
			
		||||
	size += 1 // data_coding
 | 
			
		||||
	size += 1 // sm_default_msg_id
 | 
			
		||||
	size += 1 // sm_length
 | 
			
		||||
	size += uint32(len(p.short_message) * 1)
 | 
			
		||||
	size += p.GetEncoder().EncodesInto(&p.short_message)
 | 
			
		||||
	return size
 | 
			
		||||
}
 | 
			
		||||
func (p *SUBMIT_SM) UpdateSize() {
 | 
			
		||||
	p.header.command_length = uint32(p.Size())
 | 
			
		||||
	p.sm_length = byte(len(p.short_message))
 | 
			
		||||
}
 | 
			
		||||
func (p *SUBMIT_SM) GetEncoder() encoding.Coder {
 | 
			
		||||
	switch p.data_coding {
 | 
			
		||||
	case 0b00000000: // GSM7
 | 
			
		||||
		return &encoding.GSM7Coder{}
 | 
			
		||||
	case 0b00000001: // ASCII
 | 
			
		||||
		return &encoding.ASCIICoder{}
 | 
			
		||||
	// case 0b00000011: // LATIN1
 | 
			
		||||
	// 	return &encoding.LATIN1Coder{}
 | 
			
		||||
	case 0b00001000: // UCS2
 | 
			
		||||
		return &encoding.UCS2Coder{}
 | 
			
		||||
	default:
 | 
			
		||||
		return &encoding.ASCIICoder{}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,72 @@
 | 
			
		||||
package pdu
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// region encode
 | 
			
		||||
// See examples: https://www.openmarket.com/docs/Content/apis/v4smpp/mt-examples.htm
 | 
			
		||||
func TestEncodeFunctionCorrectlyEncodesAllFields(t *testing.T) {
 | 
			
		||||
	p := &SUBMIT_SM{
 | 
			
		||||
		header: &PDU_HEADER{
 | 
			
		||||
			command_length:  0,
 | 
			
		||||
			command_id:      4,
 | 
			
		||||
			command_status:  0,
 | 
			
		||||
			sequence_number: 378019,
 | 
			
		||||
		},
 | 
			
		||||
		service_type:           "OMV4",
 | 
			
		||||
		source_addr_ton:        3,
 | 
			
		||||
		source_addr_npi:        1,
 | 
			
		||||
		source_addr:            "80362",
 | 
			
		||||
		dest_addr_ton:          1,
 | 
			
		||||
		dest_addr_npi:          1,
 | 
			
		||||
		destination_addr:       "812345001000",
 | 
			
		||||
		esm_class:              0,
 | 
			
		||||
		protocol_id:            0,
 | 
			
		||||
		priority_flag:          0,
 | 
			
		||||
		schedule_delivery_time: "",
 | 
			
		||||
		validity_period:        "180105120000004+",
 | 
			
		||||
		registered_delivery:    1,
 | 
			
		||||
		data_coding:            1, // The example uses 0 and claims to use GSM but the message is encoded as ASCII...
 | 
			
		||||
		sm_default_msg_id:      0,
 | 
			
		||||
		short_message:          "Reply Yes to opt in or No to opt out.",
 | 
			
		||||
	}
 | 
			
		||||
	p.UpdateSize()
 | 
			
		||||
	buf := ByteBufferPool.Get(p.Size())
 | 
			
		||||
	err := p.Encode(buf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("expected no error, got %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	expected := []byte{0, 0, 0, 107, 0, 0, 0, 4, 0, 0, 0, 0, 0, 5, 196, 163, 79, 77, 86, 52, 0, 3, 1, 56, 48, 51, 54, 50, 0, 1, 1, 56, 49, 50, 51, 52, 53, 48, 48, 49, 48, 48, 48, 0, 0, 0, 0, 0, 49, 56, 48, 49, 48, 53, 49, 50, 48, 48, 48, 48, 48, 48, 52, 43, 0, 1, 0, 1, 0, 37, 82, 101, 112, 108, 121, 32, 89, 101, 115, 32, 116, 111, 32, 111, 112, 116, 32, 105, 110, 32, 111, 114, 32, 78, 111, 32, 116, 111, 32, 111, 112, 116, 32, 111, 117, 116, 46}
 | 
			
		||||
	if !bytes.Equal(buf.Bytes(), expected) {
 | 
			
		||||
		t.Fatalf("expected %v, got %v", expected, buf.Bytes())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func TestRealScenario(t *testing.T) {
 | 
			
		||||
//
 | 
			
		||||
// 	p.header.command_length = uint32(p.Size())
 | 
			
		||||
// 	p.sm_length = byte(len(p.short_message))
 | 
			
		||||
// 	buf := make([]byte, p.Size())
 | 
			
		||||
// 	err := p.EncodeInto(&buf)
 | 
			
		||||
// 	if err != nil {
 | 
			
		||||
// 		t.Errorf("Expected no error, got %v", err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	if len(buf) != len(expected) {
 | 
			
		||||
// 		t.Errorf("Expected byte slice of length %d, got %d", len(expected), len(buf))
 | 
			
		||||
// 	}
 | 
			
		||||
// 	for i, v := range buf {
 | 
			
		||||
// 		if v != expected[i] {
 | 
			
		||||
// 			t.Errorf("Expected byte slice with values %v, got %v", expected, buf)
 | 
			
		||||
// 			break
 | 
			
		||||
// 		}
 | 
			
		||||
// 	}
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// region decode
 | 
			
		||||
 | 
			
		||||
// region size
 | 
			
		||||
func TestCalculateSizeTypicalInstance(t *testing.T) {
 | 
			
		||||
	p := &SUBMIT_SM{
 | 
			
		||||
@@ -14,7 +77,7 @@ func TestCalculateSizeTypicalInstance(t *testing.T) {
 | 
			
		||||
		validity_period:        "",
 | 
			
		||||
		short_message:          "Hello, World!",
 | 
			
		||||
	}
 | 
			
		||||
	expectedSize := uint32(16 + len(p.service_type) + 1 + 1 + len(p.source_addr) + 1 + 1 + len(p.destination_addr) + 1 + 1 + 1 + len(p.schedule_delivery_time) + len(p.validity_period) + 1 + 1 + 1 + 1 + 1 + len(p.short_message))
 | 
			
		||||
	expectedSize := 16 + len(p.service_type) + 1 + 1 + len(p.source_addr) + 1 + 1 + len(p.destination_addr) + 1 + 1 + 1 + len(p.schedule_delivery_time) + len(p.validity_period) + 1 + 1 + 1 + 1 + 1 + len(p.short_message)
 | 
			
		||||
	actualSize := p.Size()
 | 
			
		||||
	if actualSize != expectedSize {
 | 
			
		||||
		t.Errorf("Expected size %d, but got %d", expectedSize, actualSize)
 | 
			
		||||
@@ -31,7 +94,7 @@ func TestCalculateSizeMaxLengths(t *testing.T) {
 | 
			
		||||
		validity_period:        string(make([]byte, maxLen)),
 | 
			
		||||
		short_message:          string(make([]byte, maxLen)),
 | 
			
		||||
	}
 | 
			
		||||
	expectedSize := uint32(16 + maxLen + 1 + 1 + maxLen + 1 + 1 + maxLen + 1 + 1 + 1 + maxLen + maxLen + 1 + 1 + 1 + 1 + 1 + maxLen)
 | 
			
		||||
	expectedSize := 16 + maxLen + 1 + 1 + maxLen + 1 + 1 + maxLen + 1 + 1 + 1 + maxLen + maxLen + 1 + 1 + 1 + 1 + 1 + maxLen
 | 
			
		||||
	actualSize := p.Size()
 | 
			
		||||
	if actualSize != expectedSize {
 | 
			
		||||
		t.Errorf("Expected size %d, but got %d", expectedSize, actualSize)
 | 
			
		||||
@@ -47,9 +110,11 @@ func TestHandlesEmptyStringsForAllStringFields(t *testing.T) {
 | 
			
		||||
		validity_period:        "",
 | 
			
		||||
		short_message:          "",
 | 
			
		||||
	}
 | 
			
		||||
	expectedSize := uint32(16 + len(p.service_type) + 1 + 1 + len(p.source_addr) + 1 + 1 + len(p.destination_addr) + 1 + 1 + 1 + len(p.schedule_delivery_time) + len(p.validity_period) + 1 + 1 + 1 + 1 + 1 + len(p.short_message))
 | 
			
		||||
	expectedSize := 16 + len(p.service_type) + 1 + 1 + len(p.source_addr) + 1 + 1 + len(p.destination_addr) + 1 + 1 + 1 + len(p.schedule_delivery_time) + len(p.validity_period) + 1 + 1 + 1 + 1 + 1 + len(p.short_message)
 | 
			
		||||
	actualSize := p.Size()
 | 
			
		||||
	if actualSize != expectedSize {
 | 
			
		||||
		t.Errorf("Expected size %d, but got %d", expectedSize, actualSize)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// region benchmark
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user