You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

322 lines
11 KiB
C#

1 month ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace Unplugged.IbmBits
{
public static class IbmConverter
{
#region String
static readonly Encoding _unicode = Encoding.Unicode;
static readonly Encoding _ebcdic = Encoding.GetEncoding("IBM037");
/// <summary>
/// Returns a Unicode string converted from a byte array of EBCDIC encoded characters
/// </summary>
public static string ToString(byte[] value)
{
return ToString(value, 0);
}
/// <summary>
/// Returns a Unicode string converted from a byte array of EBCDIC encoded characters
/// starting at the specified position
/// </summary>
/// <param name="startingIndex">
/// Zero-based index of starting position in value array
/// </param>
public static string ToString(byte[] value, int startingIndex)
{
if (ReferenceEquals(null, value))
throw new ArgumentNullException("value");
return ToString(value, startingIndex, value.Length - startingIndex);
}
/// <summary>
/// Returns a Unicode string converted from a byte array of EBCDIC encoded characters
/// starting at the specified position of the given length
/// </summary>
/// <param name="startingIndex">
/// Zero-based index of starting position in value array
/// </param>
/// <param name="length">
/// Number of characters to convert
/// </param>
public static string ToString(byte[] value, int startingIndex, int length)
{
var unicodeBytes = Encoding.Convert(_ebcdic, _unicode, value, startingIndex, length);
return _unicode.GetString(unicodeBytes);
}
/// <summary>
/// Returns a byte array of EBCDIC encoded characters converted from a Unicode string
/// </summary>
public static byte[] GetBytes(string value)
{
return GetBytes(value, 0);
}
/// <summary>
/// Returns a byte array of EBCDIC encoded characters converted from a Unicode substring
/// starting at the specified position
/// </summary>
/// <param name="startingIndex">
/// Zero-based starting index of substring
/// </param>
public static byte[] GetBytes(string value, int startingIndex)
{
return GetBytes(value, startingIndex, value.Length - startingIndex);
}
/// <summary>
/// Returns a byte array of EBCDIC encoded characters converted from a Unicode substring
/// starting at the specified position with the given length
/// </summary>
/// <param name="startingIndex">
/// Zero-based starting index of substring
/// </param>
/// <param name="length">
/// Number of characters to convert
/// </param>
public static byte[] GetBytes(string value, int startingIndex, int length)
{
if (ReferenceEquals(null, value))
throw new ArgumentNullException("value");
var unicodeBytes = _unicode.GetBytes(value.ToCharArray(startingIndex, length));
return Encoding.Convert(_unicode, _ebcdic, unicodeBytes);
}
#endregion
#region Int16
/// <summary>
/// Returns a 16-bit signed integer converted from two bytes encoding a big endian 16-bit signed integer
/// </summary>
public static Int16 ToInt16(byte[] value)
{
return ToInt16(value, 0);
}
/// <summary>
/// Returns a 16-bit signed integer converted from two bytes in a specified position encoding a big endian 16-bit signed integer
/// </summary>
public static Int16 ToInt16(byte[] value, int startIndex)
{
if (ReferenceEquals(null, value))
throw new ArgumentNullException("value");
var bytes = new byte[] { value[startIndex + 1], value[startIndex] };
return BitConverter.ToInt16(bytes, 0);
}
/// <summary>
/// Returns two bytes encoding a big endian 16-bit signed integer given a 16-bit signed integer
/// </summary>
public static byte[] GetBytes(short value)
{
var b = BitConverter.GetBytes(value);
return new byte[] { b[1], b[0] };
}
#endregion
#region Int32
/// <summary>
/// Returns a 32-bit signed integer converted from four bytes encoding a big endian 32-bit signed integer
/// </summary>
public static int ToInt32(byte[] value)
{
return ToInt32(value, 0);
}
/// <summary>
/// Returns a 32-bit signed integer converted from four bytes at a specified position encoding a big endian 32-bit signed integer
/// </summary>
public static Int32 ToInt32(byte[] value, int startIndex)
{
if (ReferenceEquals(null, value))
throw new ArgumentNullException("value");
var bytes = new byte[] { value[startIndex + 3], value[startIndex + 2], value[startIndex + 1], value[startIndex] };
return BitConverter.ToInt32(bytes, 0);
}
public static byte[] GetBytes(int value)
{
var b = BitConverter.GetBytes(value);
return new byte[] { b[3], b[2], b[1], b[0] };
}
#endregion
#region Single
static readonly int _ibmBase = 16;
static readonly byte _exponentBias = 64;
static readonly int _threeByteShift = 16777216;
/// <summary>
/// Returns a 32-bit IEEE single precision floating point number from four bytes encoding
/// a single precision number in IBM System/360 Floating Point format
/// </summary>
public static Single ToSingle(byte[] value)
{
if (ReferenceEquals(null, value))
throw new ArgumentNullException("value");
if (0 == BitConverter.ToInt32(value, 0))
return 0;
// The first bit is the sign. The next 7 bits are the exponent.
byte exponentBits = value[0];
var sign = +1.0;
// Remove sign from first bit
if (exponentBits >= 128)
{
sign = -1.0;
exponentBits -= 128;
}
// Remove the bias from the exponent
// exponentBits -= _exponentBias); // 出现负数时,出错
var exponent = Math.Pow(_ibmBase, exponentBits - _exponentBias);
// The fractional part is Big Endian unsigned int to the right of the radix point
// So we reverse the bytes and pack them back into an int
var fractionBytes = new byte[] { value[3], value[2], value[1], 0 };
// Note: The sign bit for int32 is in the last byte of the array, which is zero, so we don't have to convert to uint
var mantissa = BitConverter.ToInt32(fractionBytes, 0);
// And divide by 2^(8 * 3) to move the decimal all the way to the left
var fraction = mantissa / (float)_threeByteShift;
return (float)(sign * exponent * fraction);
}
/// <summary>
/// Given a 32-bit IEEE single precision floating point number, returns four bytes encoding
/// a single precision number in IBM System/360 Floating Point format
/// </summary>
public static byte[] GetBytes(Single value)
{
var bytes = new byte[4];
if (value == 0)
return bytes;
// Sign
if (value < 0)
bytes[0] = 128;
var v = Math.Abs(value);
// Fraction
// Find the number of digits (in the IBM base) we need to move the radix point to get a value that is less than 1
var moveRadix = (int)Math.Log(v, _ibmBase) + 1;
var fraction = v / (Math.Pow(_ibmBase, moveRadix));
var fractionInt = (int)(_threeByteShift * fraction);
var fractionBytes = BitConverter.GetBytes(fractionInt);
bytes[3] = fractionBytes[0];
bytes[2] = fractionBytes[1];
bytes[1] = fractionBytes[2];
// Exponent
var exponent = moveRadix + _exponentBias;
bytes[0] += (byte)exponent;
return bytes;
}
#endregion
#region Packed Decimal
/// <summary>
/// Unpack the byte array into a decimal
/// </summary>
/// <remarks>
/// From the java version made by p4w3l located here : http://cobol2j.cvs.sourceforge.net/viewvc/cobol2j/cobol2j/src/main/java/net/sf/cobol2j/RecordSet.java?revision=1.25&view=markup from
/// the open source projet cobol2j at http://sourceforge.net/projects/cobol2j/ under LGPLV2
/// </remarks>
public static Decimal ToUnpackedDecimal(byte[] inputData, int scale)
{
var inputLength = inputData.Length;
var strbuf = new StringBuilder();
int tempData;
int tempData1;
for (int i = 0; i < inputData.Length; i++)
{
tempData = inputData[i];
tempData1 = tempData & 0xF0;
int tempData2 = tempData1 >> 4;
strbuf.Append(tempData2);
if (i < (inputLength - 1))
{
tempData = inputData[i];
tempData1 = tempData & 0x0F;
strbuf.Append(tempData1);
}
}
if ((scale > 0 && strbuf.Length -scale>0))
strbuf.Insert(strbuf.Length - scale, '.');
var result = decimal.Parse(strbuf.ToString());
tempData = inputData[inputLength - 1];
tempData1 = tempData & 0x0F;
if ((tempData1 == 0x0F) || (tempData1 == 0x0C))
return result;
if (tempData1 == 0x0D)
return -result;
return result;
}
/// <summary>
/// Convert the decimal value into its packed value
/// </summary>
/// <param name="originalValue">The value to pack</param>
/// <returns>The packed value as a byte[]</returns>
/// <remarks>
/// James Howey Copyright (c) Microsoft Corporation. All rights reserved. Microsoft Shared Source Permissive License
/// </remarks>
public static byte[] GetBytes(decimal originalValue)
{
var value = long.Parse(originalValue.ToString(CultureInfo.InvariantCulture).Replace(".", ""));
var comp3 = new Stack<byte>(10);
byte currentByte;
if (value < 0)
{
currentByte = 0x0d;
value = -value;
}
else
currentByte = 0x0c;
var byteComplete = false;
while (value != 0)
{
if (byteComplete)
currentByte = (byte)(value % 10);
else
currentByte |= (byte)((value % 10) << 4);
value /= 10;
byteComplete = !byteComplete;
if (byteComplete)
comp3.Push(currentByte);
}
if (!byteComplete)
comp3.Push(currentByte);
return comp3.ToArray();
}
#endregion
}
}