@@ -63,7 +63,7 @@ var isNegative = map[byte]bool{
63
63
64
64
// Calculate a number of digits in a buffer with decimal number.
65
65
//
66
- // Plus, minus, point and leading zeros do not count.
66
+ // Plus, minus, point and leading zeroes do not count.
67
67
// Contains a quirk for a zero - returns 1.
68
68
//
69
69
// Examples (see more examples in tests):
@@ -125,7 +125,7 @@ func EncodeStringToBCD(buf string) ([]byte, error) {
125
125
scale := 0 // By default decimal number is integer.
126
126
var byteBuf []byte
127
127
for i , ch := range []byte (buf ) {
128
- // Skip leading zeros .
128
+ // Skip leading zeroes .
129
129
if (len (byteBuf ) == 0 ) && ch == '0' {
130
130
continue
131
131
}
@@ -179,52 +179,77 @@ func EncodeStringToBCD(buf string) ([]byte, error) {
179
179
// converted to a string. The BCD array is assumed full of digits, and must be
180
180
// ended by a 4-bit sign nibble in the least significant four bits of the final
181
181
// byte. The scale is used (negated) as the exponent of the decimal number.
182
- // Note that zeros may have a sign and/or a scale.
182
+ // Note that zeroes may have a sign and/or a scale.
183
183
func DecodeStringFromBCD (bcdBuf []byte ) (string , error ) {
184
- const scaleIdx = 0 // Index of a byte with scale.
184
+ // Index of a byte with scale.
185
+ const scaleIdx = 0
185
186
scale := int (bcdBuf [scaleIdx ])
186
- // Get a BCD buffer without a byte with scale.
187
+
188
+ // Get a BCD buffer without scale.
187
189
bcdBuf = bcdBuf [scaleIdx + 1 :]
188
- length := len (bcdBuf )
189
- var digits []string
190
- for i , bcdByte := range bcdBuf {
191
- highNibble := bcdByte >> 4
192
- digits = append (digits , string ('0' + highNibble ))
193
- lowNibble := bcdByte & 0x0f
194
- // lowNibble of last byte is ignored because it contains a
195
- // sign.
196
- if i != length - 1 {
197
- digits = append (digits , string ('0' + lowNibble ))
198
- }
199
- }
190
+ bufLen := len (bcdBuf )
200
191
201
- // Add missing zeros when scale is less than current length.
202
- l := len ( digits )
203
- if scale >= l {
204
- var zeros [] string
205
- for i := 0 ; i <= scale - l ; i ++ {
206
- zeros = append ( zeros , "0" )
207
- }
208
- digits = append ( zeros , digits ... )
192
+ // Every nibble contains a digit, and the last low nibble contains a
193
+ // sign.
194
+ ndigits := bufLen * 2 - 1
195
+
196
+ // The first nibble contains 0 if the decimal number has an even number of
197
+ // digits. Decrease a number of digits if so.
198
+ if bcdBuf [ 0 ] & 0xf0 == 0 {
199
+ ndigits -= 1
209
200
}
210
201
211
- // Add a dot when number is fractional.
212
- if scale != 0 {
213
- idx := len (digits ) - scale
214
- digits = append (digits , "X" ) // [1 2 3 X]
215
- copy (digits [idx :], digits [idx - 1 :]) // [1 2 2 3]
216
- digits [idx ] = "." // [1 . 2 3]
202
+ // Reserve bytes for dot and sign.
203
+ numLen := ndigits + 2
204
+ // Reserve bytes for zeroes.
205
+ if scale >= ndigits {
206
+ numLen += scale - ndigits
217
207
}
218
208
209
+ var bld strings.Builder
210
+ bld .Grow (numLen )
211
+
219
212
// Add a sign, it is encoded in a low nibble of a last byte.
220
- lastByte := bcdBuf [length - 1 ]
213
+ lastByte := bcdBuf [bufLen - 1 ]
221
214
sign := lastByte & 0x0f
222
215
if isNegative [sign ] {
223
- digits = append ([] string { "-" }, digits ... )
216
+ bld . WriteByte ( '-' )
224
217
}
225
218
226
- // Merge slice to a single string.
227
- str := strings .Join (digits , "" )
219
+ // Add missing zeroes to the left side when scale is bigger than a
220
+ // number of digits and a single missed zero to the right side when
221
+ // equal.
222
+ if scale > ndigits {
223
+ bld .WriteByte ('0' )
224
+ bld .WriteByte ('.' )
225
+ for diff := scale - ndigits ; diff > 0 ; diff -- {
226
+ bld .WriteByte ('0' )
227
+ }
228
+ } else if scale == ndigits {
229
+ bld .WriteByte ('0' )
230
+ }
231
+
232
+ const MaxDigit = 0x09
233
+ // Builds a buffer with symbols of decimal number (digits, dot and sign).
234
+ processNibble := func (nibble byte ) {
235
+ if nibble <= MaxDigit {
236
+ if ndigits == scale {
237
+ bld .WriteByte ('.' )
238
+ }
239
+ bld .WriteByte (nibble + '0' )
240
+ ndigits --
241
+ }
242
+ }
243
+
244
+ for i , bcdByte := range bcdBuf {
245
+ highNibble := bcdByte >> 4
246
+ lowNibble := bcdByte & 0x0f
247
+ // Skip a first high nibble as no digit there.
248
+ if i != 0 || highNibble != 0 {
249
+ processNibble (highNibble )
250
+ }
251
+ processNibble (lowNibble )
252
+ }
228
253
229
- return str , nil
254
+ return bld . String () , nil
230
255
}
0 commit comments