/Languages/IronPython/IronPython.Modules/_struct.cs
C# | 1245 lines | 1060 code | 159 blank | 26 comment | 162 complexity | c4eaa49da46c95587bfafda65b203456 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
- /* ****************************************************************************
- *
- * Copyright (c) Microsoft Corporation.
- *
- * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
- * copy of the license can be found in the License.html file at the root of this distribution. If
- * you cannot locate the Apache License, Version 2.0, please send an email to
- * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
- * by the terms of the Apache License, Version 2.0.
- *
- * You must not remove this notice, or any other, from this software.
- *
- *
- * ***************************************************************************/
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading;
- using Microsoft.Scripting;
- using Microsoft.Scripting.Runtime;
- using Microsoft.Scripting.Utils;
- using IronPython.Runtime;
- using IronPython.Runtime.Exceptions;
- using IronPython.Runtime.Operations;
- using IronPython.Runtime.Types;
- using IronPython.Runtime.Binding;
- #if FEATURE_NUMERICS
- using System.Numerics;
- #else
- using Microsoft.Scripting.Math;
- #endif
- [assembly: PythonModule("_struct", typeof(IronPython.Modules.PythonStruct))]
- namespace IronPython.Modules {
- public static class PythonStruct {
- [SpecialName]
- public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
- context.EnsureModuleException("structerror", dict, "error", "struct");
- }
- #region Public API
- public const string __doc__ = null;
- public const string __version__ = "0.2";
- public const int _PY_STRUCT_FLOAT_COERCE = 0;
- public const int _PY_STRUCT_OVERFLOW_MASKING = 0;
- public const int _PY_STRUCT_RANGE_CHECKING = 0;
- [PythonType, Documentation("Represents a compiled struct pattern")]
- public class Struct : IWeakReferenceable {
- private string _formatString; // the last format string passed to __init__
- private Format[] _formats; // the various formatting options for the compiled struct
- private bool _isStandardized; // true if the format is in standardized mode
- private bool _isLittleEndian; // true if the format is in little endian mode
- private int _encodingCount = -1; // the number of objects consumed/produced by the format
- private int _encodingSize = -1; // the number of bytes read/produced by the format
- private WeakRefTracker _tracker; // storage for weak proxy's
- private void Initialize(Struct s) {
- _formatString = s._formatString;
- _formats = s._formats;
- _isStandardized = s._isStandardized;
- _isLittleEndian = s._isLittleEndian;
- _encodingCount = s._encodingCount;
- _encodingSize = s._encodingSize;
- _tracker = s._tracker;
- }
- internal Struct(CodeContext/*!*/ context, [NotNull]string/*!*/ fmt) {
- __init__(context, fmt);
- }
- #region Python construction
- [Documentation("creates a new uninitialized struct object - all arguments are ignored")]
- public Struct(params object[] args) {
- }
- [Documentation("creates a new uninitialized struct object - all arguments are ignored")]
- public Struct([ParamDictionary]IDictionary<object, object> kwArgs, params object[] args) {
- }
- [Documentation("initializes or re-initializes the compiled struct object with a new format")]
- public void __init__(CodeContext/*!*/ context, [NotNull]string/*!*/ fmt) {
- ContractUtils.RequiresNotNull(fmt, "fmt");
- _formatString = fmt;
- Struct s;
- bool gotIt;
- lock (_cache) {
- gotIt = _cache.TryGetValue(_formatString, out s);
- }
- if (gotIt) {
- Initialize(s);
- } else {
- Compile(context, fmt);
- }
- }
- #endregion
- #region Public API
- [Documentation("gets the current format string for the compiled Struct")]
- public string format {
- get {
- return _formatString;
- }
- }
- [Documentation("returns a string consisting of the values serialized according to the format of the struct object")]
- public string/*!*/ pack(CodeContext/*!*/ context, params object[] values) {
- if (values.Length != _encodingCount) {
- throw Error(context, String.Format("pack requires exactly {0} arguments", _encodingCount));
- }
- int curObj = 0;
- StringBuilder res = new StringBuilder(_encodingSize);
- for (int i = 0; i < _formats.Length; i++) {
- Format curFormat = _formats[i];
- if (!_isStandardized) {
- // In native mode, align to {size}-byte boundaries
- int nativeSize = curFormat.NativeSize;
- int alignLength = Align(res.Length, nativeSize);
- int padLength = alignLength - res.Length;
- for (int j = 0; j < padLength; j++) {
- res.Append('\0');
- }
- }
- switch (curFormat.Type) {
- case FormatType.PadByte:
- res.Append('\0', curFormat.Count);
- break;
- case FormatType.Bool:
- res.Append(GetBoolValue(context, curObj++, values) ? (char)1 : '\0');
- break;
- case FormatType.Char:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Append(GetCharValue(context, curObj++, values));
- }
- break;
- case FormatType.SignedChar:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Append((char)(byte)GetSByteValue(context, curObj++, values));
- }
- break;
- case FormatType.UnsignedChar:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Append((char)GetByteValue(context, curObj++, values));
- }
- break;
- case FormatType.Short:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteShort(res, _isLittleEndian, GetShortValue(context, curObj++, values));
- }
- break;
- case FormatType.UnsignedShort:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteUShort(res, _isLittleEndian, GetUShortValue(context, curObj++, values));
- }
- break;
- case FormatType.Int:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteInt(res, _isLittleEndian, GetIntValue(context, curObj++, values));
- }
- break;
- case FormatType.UnsignedInt:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteUInt(res, _isLittleEndian, GetULongValue(context, curObj++, values, "unsigned int"));
- }
- break;
- case FormatType.UnsignedLong:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteUInt(res, _isLittleEndian, GetULongValue(context, curObj++, values, "unsigned long"));
- }
- break;
- case FormatType.Pointer:
- for (int j = 0; j < curFormat.Count; j++) {
- WritePointer(res, _isLittleEndian, GetPointer(context, curObj++, values));
- }
- break;
- case FormatType.LongLong:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteLong(res, _isLittleEndian, GetLongValue(context, curObj++, values));
- }
- break;
- case FormatType.UnsignedLongLong:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteULong(res, _isLittleEndian, GetULongLongValue(context, curObj++, values));
- }
- break;
- case FormatType.Double:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteDouble(res, _isLittleEndian, GetDoubleValue(context, curObj++, values));
- }
- break;
- case FormatType.Float:
- for (int j = 0; j < curFormat.Count; j++) {
- WriteFloat(res, _isLittleEndian, (float)GetDoubleValue(context, curObj++, values));
- }
- break;
- case FormatType.CString:
- WriteString(res, curFormat.Count, GetStringValue(context, curObj++, values));
- break;
- case FormatType.PascalString:
- WritePascalString(res, curFormat.Count - 1, GetStringValue(context, curObj++, values));
- break;
- default:
- throw Error(context, "bad format string");
- }
- }
- return res.ToString();
- }
- [Documentation("Stores the deserialized data into the provided array")]
- public void pack_into(CodeContext/*!*/ context, [NotNull]ArrayModule.array/*!*/ buffer, int offset, params object[] args) {
- byte[] existing = buffer.ToByteArray();
- if (offset + size > existing.Length) {
- throw Error(context, String.Format("pack_into requires a buffer of at least {0} bytes", size));
- }
- string data = pack(context, args);
- for (int i = 0; i < data.Length; i++) {
- existing[i + offset] = (byte)data[i];
- }
- buffer.Clear();
- buffer.FromStream(new MemoryStream(existing));
- }
- [Documentation("deserializes the string using the structs specified format")]
- public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [NotNull]string @string) {
- if (@string.Length != size) {
- throw Error(context, String.Format("unpack requires a string argument of length {0}", size));
- }
- string data = @string;
- int curIndex = 0;
- List<object> res = new List<object>(_encodingCount);
- for (int i = 0; i < _formats.Length; i++) {
- Format curFormat = _formats[i];
- if (!_isStandardized) {
- // In native mode, align to {size}-byte boundaries
- int nativeSize = curFormat.NativeSize;
- if (nativeSize > 0) {
- curIndex = Align(curIndex, nativeSize);
- }
- }
- switch (curFormat.Type) {
- case FormatType.PadByte:
- curIndex += curFormat.Count;
- break;
- case FormatType.Bool:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(CreateBoolValue(context, ref curIndex, data));
- }
- break;
- case FormatType.Char:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(CreateCharValue(context, ref curIndex, data).ToString());
- }
- break;
- case FormatType.SignedChar:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add((int)(sbyte)CreateCharValue(context, ref curIndex, data));
- }
- break;
- case FormatType.UnsignedChar:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add((int)CreateCharValue(context, ref curIndex, data));
- }
- break;
- case FormatType.Short:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add((int)CreateShortValue(context, ref curIndex, _isLittleEndian, data));
- }
- break;
- case FormatType.UnsignedShort:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add((int)CreateUShortValue(context, ref curIndex, _isLittleEndian, data));
- }
- break;
- case FormatType.Int:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(CreateIntValue(context, ref curIndex, _isLittleEndian, data));
- }
- break;
- case FormatType.UnsignedInt:
- case FormatType.UnsignedLong:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(BigIntegerOps.__int__((BigInteger)CreateUIntValue(context, ref curIndex, _isLittleEndian, data)));
- }
- break;
- case FormatType.Pointer:
- for (int j = 0; j < curFormat.Count; j++) {
- if (IntPtr.Size == 4) {
- res.Add(CreateIntValue(context, ref curIndex, _isLittleEndian, data));
- } else {
- res.Add(BigIntegerOps.__int__((BigInteger)CreateLongValue(context, ref curIndex, _isLittleEndian, data)));
- }
- }
- break;
- case FormatType.LongLong:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(BigIntegerOps.__int__((BigInteger)CreateLongValue(context, ref curIndex, _isLittleEndian, data)));
- }
- break;
- case FormatType.UnsignedLongLong:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(BigIntegerOps.__int__((BigInteger)CreateULongValue(context, ref curIndex, _isLittleEndian, data)));
- }
- break;
- case FormatType.Float:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add((double)CreateFloatValue(context, ref curIndex, _isLittleEndian, data));
- }
- break;
- case FormatType.Double:
- for (int j = 0; j < curFormat.Count; j++) {
- res.Add(CreateDoubleValue(context, ref curIndex, _isLittleEndian, data));
- }
- break;
- case FormatType.CString:
- res.Add(CreateString(context, ref curIndex, curFormat.Count, data));
- break;
- case FormatType.PascalString:
- res.Add(CreatePascalString(context, ref curIndex, curFormat.Count - 1, data));
- break;
- }
- }
- return new PythonTuple(res);
- }
- public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]IList<byte> @string) {
- return unpack_from(context, @string, 0);
- }
- public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [NotNull]ArrayModule.array/*!*/ buffer) {
- return unpack_from(context, buffer, 0);
- }
- public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [NotNull]PythonBuffer/*!*/ buffer) {
- return unpack_from(context, buffer, 0);
- }
- [Documentation("reads the current format from the specified string")]
- public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [NotNull]string/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- int bytesAvail = buffer.Length - offset;
- if (bytesAvail < size) {
- throw Error(context, String.Format("unpack_from requires a buffer of at least {0} bytes", size));
- }
- return unpack(context, buffer.Substring(offset, size));
- }
- [Documentation("reads the current format from the specified array")]
- public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]IList<byte>/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return unpack_from(context, buffer.MakeString(), offset);
- }
- [Documentation("reads the current format from the specified array")]
- public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [NotNull]ArrayModule.array/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return unpack_from(context, buffer.ToByteArray().MakeString(), offset);
- }
- [Documentation("reads the current format from the specified buffer object")]
- public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [NotNull]PythonBuffer/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return unpack_from(context, buffer.ToString(), offset);
- }
- [Documentation("gets the number of bytes that the serialized string will occupy or are required to deserialize the data")]
- public int size {
- get {
- return _encodingSize;
- }
- }
- #endregion
- #region IWeakReferenceable Members
- WeakRefTracker IWeakReferenceable.GetWeakRef() {
- return _tracker;
- }
- bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
- return Interlocked.CompareExchange(ref _tracker, value, null) == null;
- }
- void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
- _tracker = value;
- }
- #endregion
- #region Implementation Details
- private void Compile(CodeContext/*!*/ context, string/*!*/ fmt) {
- List<Format> res = new List<Format>();
- int count = 1;
- bool fLittleEndian = BitConverter.IsLittleEndian;
- bool fStandardized = false;
- for (int i = 0; i < fmt.Length; i++) {
- switch (fmt[i]) {
- case 'x': // pad byte
- res.Add(new Format(FormatType.PadByte, count));
- count = 1;
- break;
- case '?': // bool
- res.Add(new Format(FormatType.Bool, count));
- count = 1;
- break;
- case 'c': // char
- res.Add(new Format(FormatType.Char, count));
- count = 1;
- break;
- case 'b': // signed char
- res.Add(new Format(FormatType.SignedChar, count));
- count = 1;
- break;
- case 'B': // unsigned char
- res.Add(new Format(FormatType.UnsignedChar, count));
- count = 1;
- break;
- case 'h': // short
- res.Add(new Format(FormatType.Short, count));
- count = 1;
- break;
- case 'H': // unsigned short
- res.Add(new Format(FormatType.UnsignedShort, count));
- count = 1;
- break;
- case 'i': // int
- case 'l': // long
- res.Add(new Format(FormatType.Int, count));
- count = 1;
- break;
- case 'I': // unsigned int
- res.Add(new Format(FormatType.UnsignedInt, count));
- count = 1;
- break;
- case 'L': // unsigned long
- res.Add(new Format(FormatType.UnsignedLong, count));
- count = 1;
- break;
- case 'q': // long long
- res.Add(new Format(FormatType.LongLong, count));
- count = 1;
- break;
- case 'Q': // unsigned long long
- res.Add(new Format(FormatType.UnsignedLongLong, count));
- count = 1;
- break;
- case 'f': // float
- res.Add(new Format(FormatType.Float, count));
- count = 1;
- break;
- case 'd': // double
- res.Add(new Format(FormatType.Double, count));
- count = 1;
- break;
- case 's': // char[]
- res.Add(new Format(FormatType.CString, count));
- count = 1;
- break;
- case 'p': // pascal char[]
- res.Add(new Format(FormatType.PascalString, count));
- count = 1;
- break;
- case 'P': // void *
- res.Add(new Format(FormatType.Pointer, count));
- count = 1;
- break;
- case ' ': // white space, ignore
- case '\t':
- break;
- case '=': // native
- if (i != 0) throw Error(context, "unexpected byte order");
- fStandardized = true;
- break;
- case '@': // native
- if (i != 0) throw Error(context, "unexpected byte order");
- break;
- case '<': // little endian
- if (i != 0) throw Error(context, "unexpected byte order");
- fLittleEndian = true;
- fStandardized = true;
- break;
- case '>': // big endian
- case '!': // big endian
- if (i != 0) throw Error(context, "unexpected byte order");
- fLittleEndian = false;
- fStandardized = true;
- break;
- default:
- if (Char.IsDigit(fmt[i])) {
- count = 0;
- while (Char.IsDigit(fmt[i])) {
- count = count * 10 + (fmt[i] - '0');
- i++;
- }
- if (Char.IsWhiteSpace(fmt[i])) Error(context, "white space not allowed between count and format");
- i--;
- break;
- }
- throw Error(context, "bad format string");
- }
- }
- // store the new formats
- _formats = res.ToArray();
- _isStandardized = fStandardized;
- _isLittleEndian = fLittleEndian;
- _encodingSize = _encodingCount = 0;
- for (int i = 0; i < _formats.Length; i++) {
- if (_formats[i].Type != FormatType.PadByte) {
- if (_formats[i].Type != FormatType.CString && _formats[i].Type != FormatType.PascalString) {
- _encodingCount += _formats[i].Count;
- } else {
- _encodingCount++;
- }
- }
- if (!_isStandardized) {
- // In native mode, align to {size}-byte boundaries
- _encodingSize = Align(_encodingSize, _formats[i].NativeSize);
- }
- _encodingSize += GetNativeSize(_formats[i].Type) * _formats[i].Count;
- }
- lock (_cache) {
- _cache.Add(fmt, this);
- }
- }
- #endregion
- #region Internal helpers
- internal static Struct Create(string/*!*/ format) {
- Struct res = new Struct();
- res.__init__(DefaultContext.Default, format); // default context is only used for errors, this better be an error free format.
- return res;
- }
- #endregion
- }
- #endregion
- #region Compiled Format
- /// <summary>
- /// Enum which specifies the format type for a compiled struct
- /// </summary>
- private enum FormatType {
- None,
- PadByte,
- Bool,
- Char,
- SignedChar,
- UnsignedChar,
- Short,
- UnsignedShort,
- Int,
- UnsignedInt,
- UnsignedLong,
- Float,
- LongLong,
- UnsignedLongLong,
- Double,
- CString,
- PascalString,
- Pointer,
- }
- private static int GetNativeSize(FormatType c) {
- switch (c) {
- case FormatType.Char:
- case FormatType.SignedChar:
- case FormatType.UnsignedChar:
- case FormatType.PadByte:
- case FormatType.Bool:
- case FormatType.CString:
- case FormatType.PascalString:
- return 1;
- case FormatType.Short:
- case FormatType.UnsignedShort:
- return 2;
- case FormatType.Int:
- case FormatType.UnsignedInt:
- case FormatType.UnsignedLong:
- case FormatType.Float:
- return 4;
- case FormatType.LongLong:
- case FormatType.UnsignedLongLong:
- case FormatType.Double:
- return 8;
- case FormatType.Pointer:
- return IntPtr.Size;
- default:
- throw new InvalidOperationException(c.ToString());
- }
- }
- /// <summary>
- /// Struct used to store the format and the number of times it should be repeated.
- /// </summary>
- private struct Format {
- public FormatType Type;
- public int Count;
- public Format(FormatType type, int count) {
- Type = type;
- Count = count;
- }
- public int NativeSize {
- get {
- return GetNativeSize(Type);
- }
- }
- }
- #endregion
- #region Cache of compiled struct patterns
- private const int MAX_CACHE_SIZE = 1024;
- private static CacheDict<string, Struct> _cache = new CacheDict<string, Struct>(MAX_CACHE_SIZE);
- private static Struct GetStructFromCache(CodeContext/*!*/ context, [NotNull] string fmt/*!*/) {
- Struct s;
- bool gotIt;
- lock (_cache) {
- gotIt = _cache.TryGetValue(fmt, out s);
- }
- if (!gotIt) {
- s = new Struct(context, fmt);
- }
- return s;
- }
- [Documentation("Clear the internal cache.")]
- public static void _clearcache() {
- _cache = new CacheDict<string, Struct>(MAX_CACHE_SIZE);
- }
- [Documentation("int(x[, base]) -> integer\n\nConvert a string or number to an integer, if possible. A floating point\nargument will be truncated towards zero (this does not include a string\nrepresentation of a floating point number!) When converting a string, use\nthe optional base. It is an error to supply a base when converting a\nnon-string. If base is zero, the proper base is guessed based on the\nstring content. If the argument is outside the integer range a\nlong object will be returned instead.")]
- public static int calcsize(CodeContext/*!*/ context, [NotNull]string fmt) {
- return GetStructFromCache(context, fmt).size;
- }
- [Documentation("Return string containing values v1, v2, ... packed according to fmt.")]
- public static string/*!*/ pack(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, params object[] values) {
- return GetStructFromCache(context, fmt).pack(context, values);
- }
- [Documentation("Pack the values v1, v2, ... according to fmt.\nWrite the packed bytes into the writable buffer buf starting at offset.")]
- public static void pack_into(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [NotNull]ArrayModule.array/*!*/ buffer, int offset, params object[] args) {
- GetStructFromCache(context, fmt).pack_into(context, buffer, offset, args);
- }
- [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [NotNull]string/*!*/ @string) {
- return GetStructFromCache(context, fmt).unpack(context, @string);
- }
- [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [BytesConversion][NotNull]IList<byte>/*!*/ @string) {
- return GetStructFromCache(context, fmt).unpack(context, @string);
- }
- [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [NotNull]ArrayModule.array/*!*/ buffer) {
- return GetStructFromCache(context, fmt).unpack(context, buffer);
- }
- [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]PythonBuffer/*!*/ buffer) {
- return GetStructFromCache(context, fmt).unpack(context, buffer);
- }
- [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]string/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
- }
- [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [BytesConversion][NotNull]IList<byte>/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
- }
- [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]ArrayModule.array/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
- }
- [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
- public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]PythonBuffer/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
- return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
- }
- #endregion
- #region Write Helpers
- private static void WriteShort(StringBuilder res, bool fLittleEndian, short val) {
- if (fLittleEndian) {
- res.Append((char)(val & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- } else {
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)(val & 0xff));
- }
- }
- private static void WriteUShort(StringBuilder res, bool fLittleEndian, ushort val) {
- if (fLittleEndian) {
- res.Append((char)(val & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- } else {
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)(val & 0xff));
- }
- }
- private static void WriteInt(StringBuilder res, bool fLittleEndian, int val) {
- if (fLittleEndian) {
- res.Append((char)(val & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 24) & 0xff));
- } else {
- res.Append((char)((val >> 24) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)(val & 0xff));
- }
- }
- private static void WriteUInt(StringBuilder res, bool fLittleEndian, uint val) {
- if (fLittleEndian) {
- res.Append((char)(val & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 24) & 0xff));
- } else {
- res.Append((char)((val >> 24) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)(val & 0xff));
- }
- }
- private static void WritePointer(StringBuilder res, bool fLittleEndian, IntPtr val) {
- if (IntPtr.Size == 4) {
- WriteInt(res, fLittleEndian, val.ToInt32());
- } else {
- WriteLong(res, fLittleEndian, val.ToInt64());
- }
- }
- private static void WriteFloat(StringBuilder res, bool fLittleEndian, float val) {
- byte[] bytes = BitConverter.GetBytes(val);
- if (fLittleEndian) {
- res.Append((char)bytes[0]);
- res.Append((char)bytes[1]);
- res.Append((char)bytes[2]);
- res.Append((char)bytes[3]);
- } else {
- res.Append((char)bytes[3]);
- res.Append((char)bytes[2]);
- res.Append((char)bytes[1]);
- res.Append((char)bytes[0]);
- }
- }
- private static void WriteLong(StringBuilder res, bool fLittleEndian, long val) {
- if (fLittleEndian) {
- res.Append((char)(val & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 24) & 0xff));
- res.Append((char)((val >> 32) & 0xff));
- res.Append((char)((val >> 40) & 0xff));
- res.Append((char)((val >> 48) & 0xff));
- res.Append((char)((val >> 56) & 0xff));
- } else {
- res.Append((char)((val >> 56) & 0xff));
- res.Append((char)((val >> 48) & 0xff));
- res.Append((char)((val >> 40) & 0xff));
- res.Append((char)((val >> 32) & 0xff));
- res.Append((char)((val >> 24) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)(val & 0xff));
- }
- }
- private static void WriteULong(StringBuilder res, bool fLittleEndian, ulong val) {
- if (fLittleEndian) {
- res.Append((char)(val & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 24) & 0xff));
- res.Append((char)((val >> 32) & 0xff));
- res.Append((char)((val >> 40) & 0xff));
- res.Append((char)((val >> 48) & 0xff));
- res.Append((char)((val >> 56) & 0xff));
- } else {
- res.Append((char)((val >> 56) & 0xff));
- res.Append((char)((val >> 48) & 0xff));
- res.Append((char)((val >> 40) & 0xff));
- res.Append((char)((val >> 32) & 0xff));
- res.Append((char)((val >> 24) & 0xff));
- res.Append((char)((val >> 16) & 0xff));
- res.Append((char)((val >> 8) & 0xff));
- res.Append((char)(val & 0xff));
- }
- }
- private static void WriteDouble(StringBuilder res, bool fLittleEndian, double val) {
- byte[] bytes = BitConverter.GetBytes(val);
- if (fLittleEndian) {
- res.Append((char)bytes[0]);
- res.Append((char)bytes[1]);
- res.Append((char)bytes[2]);
- res.Append((char)bytes[3]);
- res.Append((char)bytes[4]);
- res.Append((char)bytes[5]);
- res.Append((char)bytes[6]);
- res.Append((char)bytes[7]);
- } else {
- res.Append((char)bytes[7]);
- res.Append((char)bytes[6]);
- res.Append((char)bytes[5]);
- res.Append((char)bytes[4]);
- res.Append((char)bytes[3]);
- res.Append((char)bytes[2]);
- res.Append((char)bytes[1]);
- res.Append((char)bytes[0]);
- }
- }
- private static void WriteString(StringBuilder res, int len, string val) {
- for (int i = 0; i < val.Length && i < len; i++) {
- res.Append(val[i]);
- }
- for (int i = val.Length; i < len; i++) {
- res.Append('\0');
- }
- }
- private static void WritePascalString(StringBuilder res, int len, string val) {
- int lenByte = Math.Min(255, Math.Min(val.Length, len));
- res.Append((char)lenByte);
- for (int i = 0; i < val.Length && i < len; i++) {
- res.Append(val[i]);
- }
- for (int i = val.Length; i < len; i++) {
- res.Append('\0');
- }
- }
- #endregion
- #region Data getter helpers
- internal static bool GetBoolValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- object res;
- if (Converter.TryConvert(val, typeof(bool), out res)) {
- return (bool)res;
- }
- // Should never happen
- throw Error(context, "expected bool value got " + val.ToString());
- }
- internal static char GetCharValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- string strVal = val as string;
- if (strVal != null && strVal.Length == 1) return strVal[0];
- IList<Byte> byteVal = val as IList<Byte>;
- if (byteVal == null || byteVal.Count != 1) throw Error(context, "char format requires string of length 1");
- else return (char)byteVal[0];
- }
- internal static sbyte GetSByteValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- sbyte res;
- if (Converter.TryConvertToSByte(val, out res)) {
- return res;
- }
- throw Error(context, "expected sbyte value got " + val.ToString());
- }
- internal static byte GetByteValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- byte res;
- if (Converter.TryConvertToByte(val, out res)) return res;
- char cres;
- if (Converter.TryConvertToChar(val, out cres)) return (byte)cres;
- throw Error(context, "expected byte value got " + val.ToString());
- }
- internal static short GetShortValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- short res;
- if (Converter.TryConvertToInt16(val, out res)) return res;
- throw Error(context, "expected short value");
- }
- internal static ushort GetUShortValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- ushort res;
- if (Converter.TryConvertToUInt16(val, out res)) return res;
- throw Error(context, "expected ushort value");
- }
- internal static int GetIntValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- int res;
- if (Converter.TryConvertToInt32(val, out res)) return res;
- throw Error(context, "expected int value");
- }
- internal static uint GetULongValue(CodeContext/*!*/ context, int index, object[] args, string type) {
- object val = GetValue(context, index, args);
- if (val is int) {
- CheckRange(context, (int)val, type);
- return (uint)(int)val;
- } else if (val is BigInteger) {
- CheckRange(context, (BigInteger)val, type);
- return (uint)(BigInteger)val;
- } else if (val is Extensible<int>) {
- CheckRange(context, ((Extensible<int>)val).Value, type);
- return (uint)((Extensible<int>)val).Value;
- } else if (val is Extensible<BigInteger>) {
- CheckRange(context, ((Extensible<BigInteger>)val).Value, type);
- return (uint)((Extensible<BigInteger>)val).Value;
- } else {
- object objres;
- if (PythonTypeOps.TryInvokeUnaryOperator(DefaultContext.Default, val, "__int__", out objres)) {
- if (objres is int) {
- CheckRange(context, (int)objres, type);
- return (uint)(int)objres;
- }
- }
- uint res;
- if (Converter.TryConvertToUInt32(val, out res)) {
- return res;
- }
- }
- throw Error(context, "cannot convert argument to integer");
- }
- private static void CheckRange(CodeContext context, int val, string type) {
- if (val < 0) {
- OutOfRange(context, type);
- }
- }
- private static void CheckRange(CodeContext context, BigInteger bi, string type) {
- if (bi < 0 || bi > 4294967295) {
- OutOfRange(context, type);
- }
- }
- private static void OutOfRange(CodeContext context, string type) {
- throw Error(context, string.Format("integer out of range for '{0}' format code", type == "unsigned long" ? "L" : "I"));
- }
- internal static IntPtr GetPointer(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- if (IntPtr.Size == 4) {
- uint res;
- if (Converter.TryConvertToUInt32(val, out res)) {
- return new IntPtr(res);
- }
- } else {
- long res;
- if (Converter.TryConvertToInt64(val, out res)) {
- return new IntPtr(res);
- }
- }
- throw Error(context, "expected pointer value");
- }
- internal static long GetLongValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- long res;
- if (Converter.TryConvertToInt64(val, out res)) return res;
- throw Error(context, "expected long value");
- }
- internal static ulong GetULongLongValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- ulong res;
- if (Converter.TryConvertToUInt64(val, out res)) return res;
- throw Error(context, "expected ulong value");
- }
- internal static double GetDoubleValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- double res;
- if (Converter.TryConvertToDouble(val, out res)) return res;
- throw Error(context, "expected double value");
- }
- internal static string GetStringValue(CodeContext/*!*/ context, int index, object[] args) {
- object val = GetValue(context, index, args);
- string res;
- if (Converter.TryConvertToString(val, out res)) return res;
- throw Error(context, "expected string value");
- }
- internal static object GetValue(CodeContext/*!*/ context, int index, object[] args) {
- if (index >= args.Length) throw Error(context, "not enough arguments");
- return args[index];
- }
- #endregion
- #region Data creater helpers
- internal static bool CreateBoolValue(CodeContext/*!*/ context, ref int index, string data) {
- return (int)ReadData(context, ref index, data) != 0;
- }
- internal static char CreateCharValue(CodeContext/*!*/ context, ref int index, string data) {
- return ReadData(context, ref index, data);
- }
- internal static short CreateShortValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- byte b1 = (byte)ReadData(context, ref index, data);
- byte b2 = (byte)ReadData(context, ref index, data);
- if (fLittleEndian) {
- return (short)((b2 << 8) | b1);
- } else {
- return (short)((b1 << 8) | b2);
- }
- }
- internal static ushort CreateUShortValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- byte b1 = (byte)ReadData(context, ref index, data);
- byte b2 = (byte)ReadData(context, ref index, data);
- if (fLittleEndian) {
- return (ushort)((b2 << 8) | b1);
- } else {
- return (ushort)((b1 << 8) | b2);
- }
- }
- internal static float CreateFloatValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- byte[] bytes = new byte[4];
- if (fLittleEndian) {
- bytes[0] = (byte)ReadData(context, ref index, data);
- bytes[1] = (byte)ReadData(context, ref index, data);
- bytes[2] = (byte)ReadData(context, ref index, data);
- bytes[3] = (byte)ReadData(context, ref index, data);
- } else {
- bytes[3] = (byte)ReadData(context, ref index, data);
- bytes[2] = (byte)ReadData(context, ref index, data);
- bytes[1] = (byte)ReadData(context, ref index, data);
- bytes[0] = (byte)ReadData(context, ref index, data);
- }
- float res = BitConverter.ToSingle(bytes, 0);
- if (PythonContext.GetContext(context).FloatFormat == FloatFormat.Unknown) {
- if (Single.IsNaN(res) || Single.IsInfinity(res)) {
- throw PythonOps.ValueError("can't unpack IEEE 754 special value on non-IEEE platform");
- }
- }
- return res;
- }
- internal static int CreateIntValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- byte b1 = (byte)ReadData(context, ref index, data);
- byte b2 = (byte)ReadData(context, ref index, data);
- byte b3 = (byte)ReadData(context, ref index, data);
- byte b4 = (byte)ReadData(context, ref index, data);
- if (fLittleEndian)
- return (int)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
- else
- return (int)((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
- }
- internal static uint CreateUIntValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- byte b1 = (byte)ReadData(context, ref index, data);
- byte b2 = (byte)ReadData(context, ref index, data);
- byte b3 = (byte)ReadData(context, ref index, data);
- byte b4 = (byte)ReadData(context, ref index, data);
- if (fLittleEndian)
- return (uint)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
- else
- return (uint)((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
- }
- internal static long CreateLongValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- long b1 = (byte)ReadData(context, ref index, data);
- long b2 = (byte)ReadData(context, ref index, data);
- long b3 = (byte)ReadData(context, ref index, data);
- long b4 = (byte)ReadData(context, ref index, data);
- long b5 = (byte)ReadData(context, ref index, data);
- long b6 = (byte)ReadData(context, ref index, data);
- long b7 = (byte)ReadData(context, ref index, data);
- long b8 = (byte)ReadData(context, ref index, data);
- if (fLittleEndian)
- return (long)((b8 << 56) | (b7 << 48) | (b6 << 40) | (b5 << 32) |
- (b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
- else
- return (long)((b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) |
- (b5 << 24) | (b6 << 16) | (b7 << 8) | b8);
- }
- internal static ulong CreateULongValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- ulong b1 = (byte)ReadData(context, ref index, data);
- ulong b2 = (byte)ReadData(context, ref index, data);
- ulong b3 = (byte)ReadData(context, ref index, data);
- ulong b4 = (byte)ReadData(context, ref index, data);
- ulong b5 = (byte)ReadData(context, ref index, data);
- ulong b6 = (byte)ReadData(context, ref index, data);
- ulong b7 = (byte)ReadData(context, ref index, data);
- ulong b8 = (byte)ReadData(context, ref index, data);
- if (fLittleEndian)
- return (ulong)((b8 << 56) | (b7 << 48) | (b6 << 40) | (b5 << 32) |
- (b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
- else
- return (ulong)((b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) |
- (b5 << 24) | (b6 << 16) | (b7 << 8) | b8);
- }
- internal static double CreateDoubleValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
- byte[] bytes = new byte[8];
- if (fLittleEndian) {
- bytes[0] = (byte)ReadData(context, ref index, data);
- bytes[1] = (byte)ReadData(context, ref index, data);
- bytes[2] = (byte)ReadData(context, ref index, data);
- bytes[3] = (byte)ReadData(context, ref index, data);
- bytes[4] = (byte)ReadData(context, ref index, data);
- bytes[5] = (byte)ReadData(context, ref index, data);
- bytes[6] = (byte)ReadData(context, ref index, data);
- bytes[7] = (byte)ReadData(context, ref index, data);
- } else {
- bytes[7] = (byte)ReadData(context, ref index, data);
- bytes[6] = (byte)ReadData(context, ref index, data);
- bytes[5] = (byte)ReadData(context, ref index, data);
- bytes[4] = (byte)ReadData(context, ref index, data);
- bytes[3] = (byte)ReadData(context, ref index, data);
- bytes[2] = (byte)ReadData(context, ref index, data);
- bytes[1] = (byte)ReadData(context, ref index, data);
- bytes[0] = (byte)ReadData(context, ref index, data);
- }
- double res = BitConverter.ToDouble(bytes, 0);
- if (PythonContext.GetContext(context).DoubleFormat == FloatFormat.Unknown) {
- if (Double.IsNaN(res) || Double.IsInfinity(res)) {
- throw PythonOps.ValueError("can't unpack IEEE 754 special value on non-IEEE platform");
- }
- }
- return res;
- }
- internal static string CreateString(CodeContext/*!*/ context, ref int index, int count, string data) {
- StringBuilder res = new StringBuilder();
- for (int i = 0; i < count; i++) {
- res.Append(ReadData(context, ref index, data));
- }
- return res.ToString();
- }
- internal static string CreatePascalString(CodeContext/*!*/ context, ref int index, int count, string data) {
- int realLen = (int)ReadData(context, ref index, data);
- StringBuilder res = new StringBuilder();
- for (int i = 0; i < realLen; i++) {
- res.Append(ReadData(context, ref index, data));
- }
- for (int i = realLen; i < count; i++) {
- // throw away null bytes
- ReadData(context, ref index, data);
- }
- return res.ToString();
- }
- private static char ReadData(CodeContext/*!*/ context, ref int index, string data) {
- if (index >= data.Length) throw Error(context, "not enough data while reading");
- return data[index++];
- }
- #endregion
- #region Misc. Private APIs
- internal static int Align(int length, int size) {
- return length + (size - 1) & ~(size - 1);
- }
- private static Exception Error(CodeContext/*!*/ context, string msg) {
- return PythonExceptions.CreateThrowable((PythonType)PythonContext.GetContext(context).GetModuleState("structerror"), msg);
- }
- #endregion
- }
- }