Skip to content

Commit dc04925

Browse files
committed
#776 Fix PR suggestions.
1 parent 061c928 commit dc04925

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

cobol-parser/src/main/scala/za/co/absa/cobrix/cobol/parser/encoding/BCDNumberEncoders.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ object BCDNumberEncoders {
2222
/**
2323
* Encode a number as a binary encoded decimal (BCD) aka COMP-3 format to an array of bytes.
2424
*
25-
* The length of the output array is determined by the formula: (precision + 1) / 2.
25+
* Output length (bytes):
26+
* - With mandatory sign nibble (signed or unsigned): ceil((precision + 1) / 2)
27+
* - Unsigned without sign nibble: ceil(precision / 2).
2628
*
2729
* @param number The number to encode.
2830
* @param precision Total number of digits in the number.
@@ -55,27 +57,25 @@ object BCDNumberEncoders {
5557
return bytes
5658
}
5759

58-
val integralNumberStr = if (scaleFactor - scale == 0)
59-
number.setScale(0, RoundingMode.HALF_DOWN).toString
60-
else
61-
number.movePointLeft(scaleFactor - scale).setScale(0, RoundingMode.HALF_DOWN).toString
60+
val shift = scaleFactor - scale
61+
val shifted = if (shift == 0) number else number.movePointLeft(shift)
6262

63-
val isNegative = integralNumberStr.startsWith("-")
64-
val digitsOnly = integralNumberStr.stripPrefix("-").stripPrefix("+")
63+
val isNegative = number.signum() < 0
64+
val digitsOnly = shifted.abs().setScale(0, RoundingMode.HALF_DOWN).toPlainString
6565

6666
if (isNegative && (!signed || !mandatorySignNibble)) {
6767
return bytes
6868
}
6969

70+
if (digitsOnly.length > precision || scale < 0)
71+
return bytes
72+
7073
val signNibble: Byte = if (signed) {
7174
if (isNegative) 0x0D else 0x0C
7275
} else {
7376
0x0F
7477
}
7578

76-
if (digitsOnly.length > precision)
77-
return bytes
78-
7979
val padded = if (mandatorySignNibble) {
8080
if (digitsOnly.length == totalDigits - 1)
8181
digitsOnly + "0"

cobol-parser/src/test/scala/za/co/absa/cobrix/cobol/parser/encoding/BCDNumberEncodersSuite.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ class BCDNumberEncodersSuite extends AnyWordSpec {
2929
checkExpected(actual, expected)
3030
}
3131

32+
"encode a number with an even precision" in {
33+
val expected = Array[Byte](0x01, 0x23, 0x4C)
34+
val actual = BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(1234), 4, 0, 0, signed = true, mandatorySignNibble = true)
35+
36+
checkExpected(actual, expected)
37+
}
38+
3239
"encode a small number" in {
3340
val expected = Array[Byte](0x00, 0x00, 0x5C)
3441
val actual = BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(5), 5, 0, 0, signed = true, mandatorySignNibble = true)
@@ -64,6 +71,13 @@ class BCDNumberEncodersSuite extends AnyWordSpec {
6471
checkExpected(actual, expected)
6572
}
6673

74+
"encode a number without sign nibble with an even precision" in {
75+
val expected = Array[Byte](0x12, 0x34)
76+
val actual = BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(1234), 4, 0, 0, signed = true, mandatorySignNibble = false)
77+
78+
checkExpected(actual, expected)
79+
}
80+
6781
"encode a too big number" in {
6882
val expected = Array[Byte](0x00, 0x00, 0x00)
6983
val actual = BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(123456), 5, 0, 0, signed = false, mandatorySignNibble = false)
@@ -78,6 +92,13 @@ class BCDNumberEncodersSuite extends AnyWordSpec {
7892
checkExpected(actual, expected)
7993
}
8094

95+
"encode a number with nbegative scale" in {
96+
val expected = Array[Byte](0x00, 0x00, 0x00)
97+
val actual = BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(12345), 5, -1, 0, signed = false, mandatorySignNibble = false)
98+
99+
checkExpected(actual, expected)
100+
}
101+
81102
"attempt to encode a negative number without sign nibble" in {
82103
val expected = Array[Byte](0x00, 0x00, 0x00)
83104
val actual = BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(-12345), 5, 0, 0, signed = false, mandatorySignNibble = false)
@@ -105,6 +126,10 @@ class BCDNumberEncodersSuite extends AnyWordSpec {
105126

106127
checkExpected(actual, expected)
107128
}
129+
130+
"attempt to encode a number with zero prexision" in {
131+
assertThrows[IllegalArgumentException](BCDNumberEncoders.encodeBCDNumber(new java.math.BigDecimal(12345), 0, 0, 0, signed = true, mandatorySignNibble = true))
132+
}
108133
}
109134

110135
"decimal number" when {

spark-cobol/src/main/scala/za/co/absa/cobrix/spark/cobol/writer/BasicRecordCombiner.scala

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@ package za.co.absa.cobrix.spark.cobol.writer
1919
import org.apache.spark.rdd.RDD
2020
import org.apache.spark.sql.DataFrame
2121
import za.co.absa.cobrix.cobol.parser.Copybook
22+
import za.co.absa.cobrix.cobol.parser.ast.datatype.{Decimal, Integral}
2223
import za.co.absa.cobrix.cobol.parser.ast.{Group, Primitive, Statement}
2324
import za.co.absa.cobrix.cobol.reader.parameters.ReaderParameters
2425
import za.co.absa.cobrix.cobol.reader.schema.CobolSchema
25-
import za.co.absa.cobrix.cobol.parser.ast.datatype.{AlphaNumeric, COMP3, COMP3U, CobolType, Decimal, Integral}
26-
27-
import java.io.{ByteArrayOutputStream, ObjectOutputStream}
2826

2927
class BasicRecordCombiner extends RecordCombiner {
3028

@@ -34,8 +32,8 @@ class BasicRecordCombiner extends RecordCombiner {
3432
val ast = getAst(cobolSchema)
3533
val copybookFields = ast.children.filter {
3634
case p: Primitive => !p.isFiller
37-
case g: Group => !g.isFiller
38-
case _ => true
35+
case g: Group => !g.isFiller
36+
case _ => true
3937
}
4038

4139
validateSchema(df, copybookFields.toSeq)
@@ -123,8 +121,8 @@ object BasicRecordCombiner {
123121

124122
val usage = field.dataType match {
125123
case dt: Integral => dt.compact.map(_.toString).getOrElse("USAGE IS DISPLAY")
126-
case dt: Decimal => dt.compact.map(_.toString).getOrElse("USAGE IS DISPLAY")
127-
case _ => ""
124+
case dt: Decimal => dt.compact.map(_.toString).getOrElse("USAGE IS DISPLAY")
125+
case _ => ""
128126
}
129127

130128
s"$pic $usage".trim

0 commit comments

Comments
 (0)