Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ class ParserVisitor(enc: Encoding,
isFiller = identifier.toUpperCase() == Constants.FILLER,
None,
DecoderSelector.getDecoder(pic.value, stringTrimmingPolicy, isDisplayAlwaysString, effectiveEbcdicCodePage, effectiveAsciiCharset, isUtf16BigEndian = isUtf16BigEndian, floatingPointFormat, strictSignOverpunch = strictSignOverpunch, improvedNullDetection = improvedNullDetection, strictIntegralPrecision = strictIntegralPrecision),
EncoderSelector.getEncoder(pic.value, effectiveEbcdicCodePage, effectiveAsciiCharset)
EncoderSelector.getEncoder(pic.value, effectiveEbcdicCodePage, effectiveAsciiCharset, floatingPointFormat)
)(Some(parent))

parent.children.append(prim)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@

package za.co.absa.cobrix.cobol.parser.encoding

import za.co.absa.cobrix.cobol.parser.ast.datatype.{AlphaNumeric, COMP3, COMP3U, COMP4, COMP9, CobolType, Decimal, Integral, Usage}
import za.co.absa.cobrix.cobol.parser.decoders.BinaryUtils
import za.co.absa.cobrix.cobol.parser.ast.datatype.{AlphaNumeric, COMP1, COMP2, COMP3, COMP3U, COMP4, COMP9, CobolType, Decimal, Integral, Usage}
import za.co.absa.cobrix.cobol.parser.decoders.FloatingPointFormat.FloatingPointFormat
import za.co.absa.cobrix.cobol.parser.decoders.{BinaryUtils, FloatingPointFormat}
import za.co.absa.cobrix.cobol.parser.encoding.codepage.{CodePage, CodePageCommon}
import za.co.absa.cobrix.cobol.parser.position.Position

import java.nio.charset.{Charset, StandardCharsets}
import java.util

object EncoderSelector {
type Encoder = Any => Array[Byte]

def getEncoder(dataType: CobolType,
ebcdicCodePage: CodePage = new CodePageCommon,
asciiCharset: Charset = StandardCharsets.US_ASCII): Option[Encoder] = {
asciiCharset: Charset = StandardCharsets.US_ASCII,
floatingPointFormat: FloatingPointFormat = FloatingPointFormat.IBM): Option[Encoder] = {
dataType match {
case alphaNumeric: AlphaNumeric if alphaNumeric.compact.isEmpty =>
getStringEncoder(alphaNumeric.enc.getOrElse(EBCDIC), ebcdicCodePage, asciiCharset, alphaNumeric.length)
Expand All @@ -53,6 +54,10 @@ object EncoderSelector {
Option(getDisplayEncoder(integralDisplay.precision, 0, 0, integralDisplay.signPosition, isExplicitDecimalPt = false, isSignSeparate = integralDisplay.isSignSeparate))
case decimalDisplay: Decimal if decimalDisplay.compact.isEmpty =>
Option(getDisplayEncoder(decimalDisplay.precision, decimalDisplay.scale, decimalDisplay.scaleFactor, decimalDisplay.signPosition, decimalDisplay.explicitDecimal, decimalDisplay.isSignSeparate))
case decimalComp1: Decimal if decimalComp1.compact.exists(_.isInstanceOf[COMP1]) =>
Option(getSingleFloatingPointEncoder(floatingPointFormat))
case decimalComp2: Decimal if decimalComp2.compact.exists(_.isInstanceOf[COMP2]) =>
Option(getDoubleFloatingPointEncoder(floatingPointFormat))
case _ =>
None
}
Expand Down Expand Up @@ -146,4 +151,50 @@ object EncoderSelector {
}
}

def getSingleFloatingPointEncoder(floatingPointFormat: FloatingPointFormat): Encoder = {
(a: Any) => {
val number: java.lang.Float = a match {
case null => 0f
case f: Float => f
case d: Double => d.toFloat
case d: java.math.BigDecimal => d.floatValue()
case n: java.math.BigInteger => new java.math.BigDecimal(n).floatValue()
case n: Byte => n.toFloat
case n: Int => n.toFloat
case n: Long => n.toFloat
case x => x.toString.toFloat
}

floatingPointFormat match {
case FloatingPointFormat.IBM => FloatingPointEncoders.encodeIbmSingleBigEndian(number)
case FloatingPointFormat.IBM_LE => FloatingPointEncoders.encodeIbmSingleLittleEndian(number)
case FloatingPointFormat.IEEE754 => FloatingPointEncoders.encodeIeee754SingleBigEndian(number)
case FloatingPointFormat.IEEE754_LE => FloatingPointEncoders.encodeIeee754SingleLittleEndian(number)
}
}
}

def getDoubleFloatingPointEncoder(floatingPointFormat: FloatingPointFormat): Encoder = {
(a: Any) => {
val number: java.lang.Double = a match {
case null => 0d
case f: Float => f.toDouble
case d: Double => d
case d: java.math.BigDecimal => d.doubleValue()
case n: java.math.BigInteger => new java.math.BigDecimal(n).doubleValue()
case n: Byte => n.toDouble
case n: Int => n.toDouble
case n: Long => n.toDouble
case x => x.toString.toDouble
}

floatingPointFormat match {
case FloatingPointFormat.IBM => FloatingPointEncoders.encodeIbmDoubleBigEndian(number)
case FloatingPointFormat.IBM_LE => FloatingPointEncoders.encodeIbmDoubleLittleEndian(number)
case FloatingPointFormat.IEEE754 => FloatingPointEncoders.encodeIeee754DoubleBigEndian(number)
case FloatingPointFormat.IEEE754_LE => FloatingPointEncoders.encodeIeee754DoubleLittleEndian(number)
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* Copyright 2018 ABSA Group Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package za.co.absa.cobrix.cobol.parser.encoding

import java.nio.{ByteBuffer, ByteOrder}

object FloatingPointEncoders {
/**
* An encoder for IEEE-754 64 bit little-endian floats
*
* @param f A single precision floating point number.
* @return An array of 8 bytes representing the number.
*/
def encodeIeee754SingleLittleEndian(f: Float): Array[Byte] = {
val byteBuffer = ByteBuffer.allocate(4)
byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
byteBuffer.putFloat(f)
byteBuffer.array()
}

/**
* An encoder for IEEE-754 64 bit little-endian floats
*
* @param d A double precision floating point number.
* @return An array of 8 bytes representing the number.
*/
def encodeIeee754DoubleLittleEndian(d: Double): Array[Byte] = {
val byteBuffer = ByteBuffer.allocate(8)
byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
byteBuffer.putDouble(d)
byteBuffer.array()
}

/**
* An encoder for IEEE-754 64 bit big-endian floats
*
* @param f A single precision floating point number.
* @return An array of 8 bytes representing the number.
*/
def encodeIeee754SingleBigEndian(f: Float): Array[Byte] = {
val byteBuffer = ByteBuffer.allocate(4)
byteBuffer.order(ByteOrder.BIG_ENDIAN)
byteBuffer.putFloat(f)
byteBuffer.array()
}

/**
* An encoder for IEEE-754 64 bit big-endian floats
*
* @param d A double precision floating point number.
* @return An array of 8 bytes representing the number.
*/
def encodeIeee754DoubleBigEndian(d: Double): Array[Byte] = {
val byteBuffer = ByteBuffer.allocate(8)
byteBuffer.order(ByteOrder.BIG_ENDIAN)
byteBuffer.putDouble(d)
byteBuffer.array()
}

/**
* Encode a float into IBM single precision big endian format (4 bytes).
*
* IBM single precision hex float format:
* - 1 bit sign
* - 7 bits exponent (base-16, biased by 64)
* - 24 bits fraction (normalized so that the high-order nibble is non-zero)
*
* @param f A single precision floating point number.
* @return An array of 4 bytes in IBM single precision big-endian format.
*/
def encodeIbmSingleBigEndian(f: Float): Array[Byte] = {
if (f == 0.0f) {
return Array[Byte](0, 0, 0, 0)
}

if (f.isNaN) {
return Array[Byte](0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte)
}

if (f.isInfinite) {
if (f > 0)
return Array[Byte](0x7F.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte)
else
return Array[Byte](0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte)
}

val sign: Int = if (f < 0) 0x80 else 0x00
var absVal = Math.abs(f).toDouble

// Determine the base-16 exponent
var exp16 = 0
if (absVal >= 1.0) {
while (absVal >= 1.0) {
absVal /= 16.0
exp16 += 1
}
} else {
while (absVal < (1.0 / 16.0)) {
absVal *= 16.0
exp16 -= 1
}
}

val biasedExp = exp16 + 64

// fraction is in [1/16, 1), scale to 24-bit integer
val fracInt = (absVal * (1 << 24)).toLong & 0xFFFFFFL

val b0 = (sign | (biasedExp & 0x7F)).toByte
val b1 = ((fracInt >> 16) & 0xFF).toByte
val b2 = ((fracInt >> 8) & 0xFF).toByte
val b3 = (fracInt & 0xFF).toByte

Array(b0, b1, b2, b3)
}

/**
* Encode a double into IBM double precision big endian format (8 bytes).
*
* IBM double precision hex float format:
* - 1 bit sign
* - 7 bits exponent (base-16, biased by 64)
* - 56 bits fraction (normalized so that the high-order nibble is non-zero)
*
* @param d A double precision floating point number.
* @return An array of 8 bytes in IBM double precision big-endian format.
*/
def encodeIbmDoubleBigEndian(d: Double): Array[Byte] = {
if (d == 0.0) {
return Array[Byte](0, 0, 0, 0, 0, 0, 0, 0)
}

if (d.isNaN) {
return Array[Byte](0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte)
}

if (d.isInfinite) {
if (d > 0)
return Array[Byte](0x7F.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte)
else
return Array[Byte](0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte, 0xFF.toByte)
}

val sign: Int = if (d < 0) 0x80 else 0x00
var absVal = Math.abs(d)

// Determine the base-16 exponent
var exp16 = 0
if (absVal >= 1.0) {
while (absVal >= 1.0) {
absVal /= 16.0
exp16 += 1
}
} else {
while (absVal < (1.0 / 16.0)) {
absVal *= 16.0
exp16 -= 1
}
}

val biasedExp = exp16 + 64

// fraction is in [1/16, 1), scale to 56-bit integer
val fracLong = (absVal * (1L << 56).toDouble).toLong & 0x00FFFFFFFFFFFFFFL

val b0 = (sign | (biasedExp & 0x7F)).toByte
val b1 = ((fracLong >> 48) & 0xFF).toByte
val b2 = ((fracLong >> 40) & 0xFF).toByte
val b3 = ((fracLong >> 32) & 0xFF).toByte
val b4 = ((fracLong >> 24) & 0xFF).toByte
val b5 = ((fracLong >> 16) & 0xFF).toByte
val b6 = ((fracLong >> 8) & 0xFF).toByte
val b7 = (fracLong & 0xFF).toByte

Array(b0, b1, b2, b3, b4, b5, b6, b7)
}

/** Encode a float into IBM single precision little endian format (4 bytes). */
def encodeIbmSingleLittleEndian(f: Float): Array[Byte] = {
encodeIbmSingleBigEndian(f).reverse
}

/** Encode a double into IBM double precision little endian format (8 bytes). */
def encodeIbmDoubleLittleEndian(d: Double): Array[Byte] = {
encodeIbmDoubleBigEndian(d).reverse
}
}
Loading
Loading