PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython.Modules/_struct.cs

http://github.com/IronLanguages/main
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

Large files files are truncated, but you can click here to view the full file

  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections.Generic;
  17. using System.IO;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  20. using System.Text;
  21. using System.Threading;
  22. using Microsoft.Scripting;
  23. using Microsoft.Scripting.Runtime;
  24. using Microsoft.Scripting.Utils;
  25. using IronPython.Runtime;
  26. using IronPython.Runtime.Exceptions;
  27. using IronPython.Runtime.Operations;
  28. using IronPython.Runtime.Types;
  29. using IronPython.Runtime.Binding;
  30. #if FEATURE_NUMERICS
  31. using System.Numerics;
  32. #else
  33. using Microsoft.Scripting.Math;
  34. #endif
  35. [assembly: PythonModule("_struct", typeof(IronPython.Modules.PythonStruct))]
  36. namespace IronPython.Modules {
  37. public static class PythonStruct {
  38. [SpecialName]
  39. public static void PerformModuleReload(PythonContext/*!*/ context, PythonDictionary/*!*/ dict) {
  40. context.EnsureModuleException("structerror", dict, "error", "struct");
  41. }
  42. #region Public API
  43. public const string __doc__ = null;
  44. public const string __version__ = "0.2";
  45. public const int _PY_STRUCT_FLOAT_COERCE = 0;
  46. public const int _PY_STRUCT_OVERFLOW_MASKING = 0;
  47. public const int _PY_STRUCT_RANGE_CHECKING = 0;
  48. [PythonType, Documentation("Represents a compiled struct pattern")]
  49. public class Struct : IWeakReferenceable {
  50. private string _formatString; // the last format string passed to __init__
  51. private Format[] _formats; // the various formatting options for the compiled struct
  52. private bool _isStandardized; // true if the format is in standardized mode
  53. private bool _isLittleEndian; // true if the format is in little endian mode
  54. private int _encodingCount = -1; // the number of objects consumed/produced by the format
  55. private int _encodingSize = -1; // the number of bytes read/produced by the format
  56. private WeakRefTracker _tracker; // storage for weak proxy's
  57. private void Initialize(Struct s) {
  58. _formatString = s._formatString;
  59. _formats = s._formats;
  60. _isStandardized = s._isStandardized;
  61. _isLittleEndian = s._isLittleEndian;
  62. _encodingCount = s._encodingCount;
  63. _encodingSize = s._encodingSize;
  64. _tracker = s._tracker;
  65. }
  66. internal Struct(CodeContext/*!*/ context, [NotNull]string/*!*/ fmt) {
  67. __init__(context, fmt);
  68. }
  69. #region Python construction
  70. [Documentation("creates a new uninitialized struct object - all arguments are ignored")]
  71. public Struct(params object[] args) {
  72. }
  73. [Documentation("creates a new uninitialized struct object - all arguments are ignored")]
  74. public Struct([ParamDictionary]IDictionary<object, object> kwArgs, params object[] args) {
  75. }
  76. [Documentation("initializes or re-initializes the compiled struct object with a new format")]
  77. public void __init__(CodeContext/*!*/ context, [NotNull]string/*!*/ fmt) {
  78. ContractUtils.RequiresNotNull(fmt, "fmt");
  79. _formatString = fmt;
  80. Struct s;
  81. bool gotIt;
  82. lock (_cache) {
  83. gotIt = _cache.TryGetValue(_formatString, out s);
  84. }
  85. if (gotIt) {
  86. Initialize(s);
  87. } else {
  88. Compile(context, fmt);
  89. }
  90. }
  91. #endregion
  92. #region Public API
  93. [Documentation("gets the current format string for the compiled Struct")]
  94. public string format {
  95. get {
  96. return _formatString;
  97. }
  98. }
  99. [Documentation("returns a string consisting of the values serialized according to the format of the struct object")]
  100. public string/*!*/ pack(CodeContext/*!*/ context, params object[] values) {
  101. if (values.Length != _encodingCount) {
  102. throw Error(context, String.Format("pack requires exactly {0} arguments", _encodingCount));
  103. }
  104. int curObj = 0;
  105. StringBuilder res = new StringBuilder(_encodingSize);
  106. for (int i = 0; i < _formats.Length; i++) {
  107. Format curFormat = _formats[i];
  108. if (!_isStandardized) {
  109. // In native mode, align to {size}-byte boundaries
  110. int nativeSize = curFormat.NativeSize;
  111. int alignLength = Align(res.Length, nativeSize);
  112. int padLength = alignLength - res.Length;
  113. for (int j = 0; j < padLength; j++) {
  114. res.Append('\0');
  115. }
  116. }
  117. switch (curFormat.Type) {
  118. case FormatType.PadByte:
  119. res.Append('\0', curFormat.Count);
  120. break;
  121. case FormatType.Bool:
  122. res.Append(GetBoolValue(context, curObj++, values) ? (char)1 : '\0');
  123. break;
  124. case FormatType.Char:
  125. for (int j = 0; j < curFormat.Count; j++) {
  126. res.Append(GetCharValue(context, curObj++, values));
  127. }
  128. break;
  129. case FormatType.SignedChar:
  130. for (int j = 0; j < curFormat.Count; j++) {
  131. res.Append((char)(byte)GetSByteValue(context, curObj++, values));
  132. }
  133. break;
  134. case FormatType.UnsignedChar:
  135. for (int j = 0; j < curFormat.Count; j++) {
  136. res.Append((char)GetByteValue(context, curObj++, values));
  137. }
  138. break;
  139. case FormatType.Short:
  140. for (int j = 0; j < curFormat.Count; j++) {
  141. WriteShort(res, _isLittleEndian, GetShortValue(context, curObj++, values));
  142. }
  143. break;
  144. case FormatType.UnsignedShort:
  145. for (int j = 0; j < curFormat.Count; j++) {
  146. WriteUShort(res, _isLittleEndian, GetUShortValue(context, curObj++, values));
  147. }
  148. break;
  149. case FormatType.Int:
  150. for (int j = 0; j < curFormat.Count; j++) {
  151. WriteInt(res, _isLittleEndian, GetIntValue(context, curObj++, values));
  152. }
  153. break;
  154. case FormatType.UnsignedInt:
  155. for (int j = 0; j < curFormat.Count; j++) {
  156. WriteUInt(res, _isLittleEndian, GetULongValue(context, curObj++, values, "unsigned int"));
  157. }
  158. break;
  159. case FormatType.UnsignedLong:
  160. for (int j = 0; j < curFormat.Count; j++) {
  161. WriteUInt(res, _isLittleEndian, GetULongValue(context, curObj++, values, "unsigned long"));
  162. }
  163. break;
  164. case FormatType.Pointer:
  165. for (int j = 0; j < curFormat.Count; j++) {
  166. WritePointer(res, _isLittleEndian, GetPointer(context, curObj++, values));
  167. }
  168. break;
  169. case FormatType.LongLong:
  170. for (int j = 0; j < curFormat.Count; j++) {
  171. WriteLong(res, _isLittleEndian, GetLongValue(context, curObj++, values));
  172. }
  173. break;
  174. case FormatType.UnsignedLongLong:
  175. for (int j = 0; j < curFormat.Count; j++) {
  176. WriteULong(res, _isLittleEndian, GetULongLongValue(context, curObj++, values));
  177. }
  178. break;
  179. case FormatType.Double:
  180. for (int j = 0; j < curFormat.Count; j++) {
  181. WriteDouble(res, _isLittleEndian, GetDoubleValue(context, curObj++, values));
  182. }
  183. break;
  184. case FormatType.Float:
  185. for (int j = 0; j < curFormat.Count; j++) {
  186. WriteFloat(res, _isLittleEndian, (float)GetDoubleValue(context, curObj++, values));
  187. }
  188. break;
  189. case FormatType.CString:
  190. WriteString(res, curFormat.Count, GetStringValue(context, curObj++, values));
  191. break;
  192. case FormatType.PascalString:
  193. WritePascalString(res, curFormat.Count - 1, GetStringValue(context, curObj++, values));
  194. break;
  195. default:
  196. throw Error(context, "bad format string");
  197. }
  198. }
  199. return res.ToString();
  200. }
  201. [Documentation("Stores the deserialized data into the provided array")]
  202. public void pack_into(CodeContext/*!*/ context, [NotNull]ArrayModule.array/*!*/ buffer, int offset, params object[] args) {
  203. byte[] existing = buffer.ToByteArray();
  204. if (offset + size > existing.Length) {
  205. throw Error(context, String.Format("pack_into requires a buffer of at least {0} bytes", size));
  206. }
  207. string data = pack(context, args);
  208. for (int i = 0; i < data.Length; i++) {
  209. existing[i + offset] = (byte)data[i];
  210. }
  211. buffer.Clear();
  212. buffer.FromStream(new MemoryStream(existing));
  213. }
  214. [Documentation("deserializes the string using the structs specified format")]
  215. public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [NotNull]string @string) {
  216. if (@string.Length != size) {
  217. throw Error(context, String.Format("unpack requires a string argument of length {0}", size));
  218. }
  219. string data = @string;
  220. int curIndex = 0;
  221. List<object> res = new List<object>(_encodingCount);
  222. for (int i = 0; i < _formats.Length; i++) {
  223. Format curFormat = _formats[i];
  224. if (!_isStandardized) {
  225. // In native mode, align to {size}-byte boundaries
  226. int nativeSize = curFormat.NativeSize;
  227. if (nativeSize > 0) {
  228. curIndex = Align(curIndex, nativeSize);
  229. }
  230. }
  231. switch (curFormat.Type) {
  232. case FormatType.PadByte:
  233. curIndex += curFormat.Count;
  234. break;
  235. case FormatType.Bool:
  236. for (int j = 0; j < curFormat.Count; j++) {
  237. res.Add(CreateBoolValue(context, ref curIndex, data));
  238. }
  239. break;
  240. case FormatType.Char:
  241. for (int j = 0; j < curFormat.Count; j++) {
  242. res.Add(CreateCharValue(context, ref curIndex, data).ToString());
  243. }
  244. break;
  245. case FormatType.SignedChar:
  246. for (int j = 0; j < curFormat.Count; j++) {
  247. res.Add((int)(sbyte)CreateCharValue(context, ref curIndex, data));
  248. }
  249. break;
  250. case FormatType.UnsignedChar:
  251. for (int j = 0; j < curFormat.Count; j++) {
  252. res.Add((int)CreateCharValue(context, ref curIndex, data));
  253. }
  254. break;
  255. case FormatType.Short:
  256. for (int j = 0; j < curFormat.Count; j++) {
  257. res.Add((int)CreateShortValue(context, ref curIndex, _isLittleEndian, data));
  258. }
  259. break;
  260. case FormatType.UnsignedShort:
  261. for (int j = 0; j < curFormat.Count; j++) {
  262. res.Add((int)CreateUShortValue(context, ref curIndex, _isLittleEndian, data));
  263. }
  264. break;
  265. case FormatType.Int:
  266. for (int j = 0; j < curFormat.Count; j++) {
  267. res.Add(CreateIntValue(context, ref curIndex, _isLittleEndian, data));
  268. }
  269. break;
  270. case FormatType.UnsignedInt:
  271. case FormatType.UnsignedLong:
  272. for (int j = 0; j < curFormat.Count; j++) {
  273. res.Add(BigIntegerOps.__int__((BigInteger)CreateUIntValue(context, ref curIndex, _isLittleEndian, data)));
  274. }
  275. break;
  276. case FormatType.Pointer:
  277. for (int j = 0; j < curFormat.Count; j++) {
  278. if (IntPtr.Size == 4) {
  279. res.Add(CreateIntValue(context, ref curIndex, _isLittleEndian, data));
  280. } else {
  281. res.Add(BigIntegerOps.__int__((BigInteger)CreateLongValue(context, ref curIndex, _isLittleEndian, data)));
  282. }
  283. }
  284. break;
  285. case FormatType.LongLong:
  286. for (int j = 0; j < curFormat.Count; j++) {
  287. res.Add(BigIntegerOps.__int__((BigInteger)CreateLongValue(context, ref curIndex, _isLittleEndian, data)));
  288. }
  289. break;
  290. case FormatType.UnsignedLongLong:
  291. for (int j = 0; j < curFormat.Count; j++) {
  292. res.Add(BigIntegerOps.__int__((BigInteger)CreateULongValue(context, ref curIndex, _isLittleEndian, data)));
  293. }
  294. break;
  295. case FormatType.Float:
  296. for (int j = 0; j < curFormat.Count; j++) {
  297. res.Add((double)CreateFloatValue(context, ref curIndex, _isLittleEndian, data));
  298. }
  299. break;
  300. case FormatType.Double:
  301. for (int j = 0; j < curFormat.Count; j++) {
  302. res.Add(CreateDoubleValue(context, ref curIndex, _isLittleEndian, data));
  303. }
  304. break;
  305. case FormatType.CString:
  306. res.Add(CreateString(context, ref curIndex, curFormat.Count, data));
  307. break;
  308. case FormatType.PascalString:
  309. res.Add(CreatePascalString(context, ref curIndex, curFormat.Count - 1, data));
  310. break;
  311. }
  312. }
  313. return new PythonTuple(res);
  314. }
  315. public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]IList<byte> @string) {
  316. return unpack_from(context, @string, 0);
  317. }
  318. public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [NotNull]ArrayModule.array/*!*/ buffer) {
  319. return unpack_from(context, buffer, 0);
  320. }
  321. public PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [NotNull]PythonBuffer/*!*/ buffer) {
  322. return unpack_from(context, buffer, 0);
  323. }
  324. [Documentation("reads the current format from the specified string")]
  325. public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [NotNull]string/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  326. int bytesAvail = buffer.Length - offset;
  327. if (bytesAvail < size) {
  328. throw Error(context, String.Format("unpack_from requires a buffer of at least {0} bytes", size));
  329. }
  330. return unpack(context, buffer.Substring(offset, size));
  331. }
  332. [Documentation("reads the current format from the specified array")]
  333. public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]IList<byte>/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  334. return unpack_from(context, buffer.MakeString(), offset);
  335. }
  336. [Documentation("reads the current format from the specified array")]
  337. public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [NotNull]ArrayModule.array/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  338. return unpack_from(context, buffer.ToByteArray().MakeString(), offset);
  339. }
  340. [Documentation("reads the current format from the specified buffer object")]
  341. public PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [NotNull]PythonBuffer/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  342. return unpack_from(context, buffer.ToString(), offset);
  343. }
  344. [Documentation("gets the number of bytes that the serialized string will occupy or are required to deserialize the data")]
  345. public int size {
  346. get {
  347. return _encodingSize;
  348. }
  349. }
  350. #endregion
  351. #region IWeakReferenceable Members
  352. WeakRefTracker IWeakReferenceable.GetWeakRef() {
  353. return _tracker;
  354. }
  355. bool IWeakReferenceable.SetWeakRef(WeakRefTracker value) {
  356. return Interlocked.CompareExchange(ref _tracker, value, null) == null;
  357. }
  358. void IWeakReferenceable.SetFinalizer(WeakRefTracker value) {
  359. _tracker = value;
  360. }
  361. #endregion
  362. #region Implementation Details
  363. private void Compile(CodeContext/*!*/ context, string/*!*/ fmt) {
  364. List<Format> res = new List<Format>();
  365. int count = 1;
  366. bool fLittleEndian = BitConverter.IsLittleEndian;
  367. bool fStandardized = false;
  368. for (int i = 0; i < fmt.Length; i++) {
  369. switch (fmt[i]) {
  370. case 'x': // pad byte
  371. res.Add(new Format(FormatType.PadByte, count));
  372. count = 1;
  373. break;
  374. case '?': // bool
  375. res.Add(new Format(FormatType.Bool, count));
  376. count = 1;
  377. break;
  378. case 'c': // char
  379. res.Add(new Format(FormatType.Char, count));
  380. count = 1;
  381. break;
  382. case 'b': // signed char
  383. res.Add(new Format(FormatType.SignedChar, count));
  384. count = 1;
  385. break;
  386. case 'B': // unsigned char
  387. res.Add(new Format(FormatType.UnsignedChar, count));
  388. count = 1;
  389. break;
  390. case 'h': // short
  391. res.Add(new Format(FormatType.Short, count));
  392. count = 1;
  393. break;
  394. case 'H': // unsigned short
  395. res.Add(new Format(FormatType.UnsignedShort, count));
  396. count = 1;
  397. break;
  398. case 'i': // int
  399. case 'l': // long
  400. res.Add(new Format(FormatType.Int, count));
  401. count = 1;
  402. break;
  403. case 'I': // unsigned int
  404. res.Add(new Format(FormatType.UnsignedInt, count));
  405. count = 1;
  406. break;
  407. case 'L': // unsigned long
  408. res.Add(new Format(FormatType.UnsignedLong, count));
  409. count = 1;
  410. break;
  411. case 'q': // long long
  412. res.Add(new Format(FormatType.LongLong, count));
  413. count = 1;
  414. break;
  415. case 'Q': // unsigned long long
  416. res.Add(new Format(FormatType.UnsignedLongLong, count));
  417. count = 1;
  418. break;
  419. case 'f': // float
  420. res.Add(new Format(FormatType.Float, count));
  421. count = 1;
  422. break;
  423. case 'd': // double
  424. res.Add(new Format(FormatType.Double, count));
  425. count = 1;
  426. break;
  427. case 's': // char[]
  428. res.Add(new Format(FormatType.CString, count));
  429. count = 1;
  430. break;
  431. case 'p': // pascal char[]
  432. res.Add(new Format(FormatType.PascalString, count));
  433. count = 1;
  434. break;
  435. case 'P': // void *
  436. res.Add(new Format(FormatType.Pointer, count));
  437. count = 1;
  438. break;
  439. case ' ': // white space, ignore
  440. case '\t':
  441. break;
  442. case '=': // native
  443. if (i != 0) throw Error(context, "unexpected byte order");
  444. fStandardized = true;
  445. break;
  446. case '@': // native
  447. if (i != 0) throw Error(context, "unexpected byte order");
  448. break;
  449. case '<': // little endian
  450. if (i != 0) throw Error(context, "unexpected byte order");
  451. fLittleEndian = true;
  452. fStandardized = true;
  453. break;
  454. case '>': // big endian
  455. case '!': // big endian
  456. if (i != 0) throw Error(context, "unexpected byte order");
  457. fLittleEndian = false;
  458. fStandardized = true;
  459. break;
  460. default:
  461. if (Char.IsDigit(fmt[i])) {
  462. count = 0;
  463. while (Char.IsDigit(fmt[i])) {
  464. count = count * 10 + (fmt[i] - '0');
  465. i++;
  466. }
  467. if (Char.IsWhiteSpace(fmt[i])) Error(context, "white space not allowed between count and format");
  468. i--;
  469. break;
  470. }
  471. throw Error(context, "bad format string");
  472. }
  473. }
  474. // store the new formats
  475. _formats = res.ToArray();
  476. _isStandardized = fStandardized;
  477. _isLittleEndian = fLittleEndian;
  478. _encodingSize = _encodingCount = 0;
  479. for (int i = 0; i < _formats.Length; i++) {
  480. if (_formats[i].Type != FormatType.PadByte) {
  481. if (_formats[i].Type != FormatType.CString && _formats[i].Type != FormatType.PascalString) {
  482. _encodingCount += _formats[i].Count;
  483. } else {
  484. _encodingCount++;
  485. }
  486. }
  487. if (!_isStandardized) {
  488. // In native mode, align to {size}-byte boundaries
  489. _encodingSize = Align(_encodingSize, _formats[i].NativeSize);
  490. }
  491. _encodingSize += GetNativeSize(_formats[i].Type) * _formats[i].Count;
  492. }
  493. lock (_cache) {
  494. _cache.Add(fmt, this);
  495. }
  496. }
  497. #endregion
  498. #region Internal helpers
  499. internal static Struct Create(string/*!*/ format) {
  500. Struct res = new Struct();
  501. res.__init__(DefaultContext.Default, format); // default context is only used for errors, this better be an error free format.
  502. return res;
  503. }
  504. #endregion
  505. }
  506. #endregion
  507. #region Compiled Format
  508. /// <summary>
  509. /// Enum which specifies the format type for a compiled struct
  510. /// </summary>
  511. private enum FormatType {
  512. None,
  513. PadByte,
  514. Bool,
  515. Char,
  516. SignedChar,
  517. UnsignedChar,
  518. Short,
  519. UnsignedShort,
  520. Int,
  521. UnsignedInt,
  522. UnsignedLong,
  523. Float,
  524. LongLong,
  525. UnsignedLongLong,
  526. Double,
  527. CString,
  528. PascalString,
  529. Pointer,
  530. }
  531. private static int GetNativeSize(FormatType c) {
  532. switch (c) {
  533. case FormatType.Char:
  534. case FormatType.SignedChar:
  535. case FormatType.UnsignedChar:
  536. case FormatType.PadByte:
  537. case FormatType.Bool:
  538. case FormatType.CString:
  539. case FormatType.PascalString:
  540. return 1;
  541. case FormatType.Short:
  542. case FormatType.UnsignedShort:
  543. return 2;
  544. case FormatType.Int:
  545. case FormatType.UnsignedInt:
  546. case FormatType.UnsignedLong:
  547. case FormatType.Float:
  548. return 4;
  549. case FormatType.LongLong:
  550. case FormatType.UnsignedLongLong:
  551. case FormatType.Double:
  552. return 8;
  553. case FormatType.Pointer:
  554. return IntPtr.Size;
  555. default:
  556. throw new InvalidOperationException(c.ToString());
  557. }
  558. }
  559. /// <summary>
  560. /// Struct used to store the format and the number of times it should be repeated.
  561. /// </summary>
  562. private struct Format {
  563. public FormatType Type;
  564. public int Count;
  565. public Format(FormatType type, int count) {
  566. Type = type;
  567. Count = count;
  568. }
  569. public int NativeSize {
  570. get {
  571. return GetNativeSize(Type);
  572. }
  573. }
  574. }
  575. #endregion
  576. #region Cache of compiled struct patterns
  577. private const int MAX_CACHE_SIZE = 1024;
  578. private static CacheDict<string, Struct> _cache = new CacheDict<string, Struct>(MAX_CACHE_SIZE);
  579. private static Struct GetStructFromCache(CodeContext/*!*/ context, [NotNull] string fmt/*!*/) {
  580. Struct s;
  581. bool gotIt;
  582. lock (_cache) {
  583. gotIt = _cache.TryGetValue(fmt, out s);
  584. }
  585. if (!gotIt) {
  586. s = new Struct(context, fmt);
  587. }
  588. return s;
  589. }
  590. [Documentation("Clear the internal cache.")]
  591. public static void _clearcache() {
  592. _cache = new CacheDict<string, Struct>(MAX_CACHE_SIZE);
  593. }
  594. [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.")]
  595. public static int calcsize(CodeContext/*!*/ context, [NotNull]string fmt) {
  596. return GetStructFromCache(context, fmt).size;
  597. }
  598. [Documentation("Return string containing values v1, v2, ... packed according to fmt.")]
  599. public static string/*!*/ pack(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, params object[] values) {
  600. return GetStructFromCache(context, fmt).pack(context, values);
  601. }
  602. [Documentation("Pack the values v1, v2, ... according to fmt.\nWrite the packed bytes into the writable buffer buf starting at offset.")]
  603. public static void pack_into(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [NotNull]ArrayModule.array/*!*/ buffer, int offset, params object[] args) {
  604. GetStructFromCache(context, fmt).pack_into(context, buffer, offset, args);
  605. }
  606. [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
  607. public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [NotNull]string/*!*/ @string) {
  608. return GetStructFromCache(context, fmt).unpack(context, @string);
  609. }
  610. [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
  611. public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [BytesConversion][NotNull]IList<byte>/*!*/ @string) {
  612. return GetStructFromCache(context, fmt).unpack(context, @string);
  613. }
  614. [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
  615. public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string/*!*/ fmt, [NotNull]ArrayModule.array/*!*/ buffer) {
  616. return GetStructFromCache(context, fmt).unpack(context, buffer);
  617. }
  618. [Documentation("Unpack the string containing packed C structure data, according to fmt.\nRequires len(string) == calcsize(fmt).")]
  619. public static PythonTuple/*!*/ unpack(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]PythonBuffer/*!*/ buffer) {
  620. return GetStructFromCache(context, fmt).unpack(context, buffer);
  621. }
  622. [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
  623. public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]string/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  624. return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
  625. }
  626. [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
  627. public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [BytesConversion][NotNull]IList<byte>/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  628. return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
  629. }
  630. [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
  631. public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]ArrayModule.array/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  632. return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
  633. }
  634. [Documentation("Unpack the buffer, containing packed C structure data, according to\nfmt, starting at offset. Requires len(buffer[offset:]) >= calcsize(fmt).")]
  635. public static PythonTuple/*!*/ unpack_from(CodeContext/*!*/ context, [BytesConversion][NotNull]string fmt/*!*/, [NotNull]PythonBuffer/*!*/ buffer, [DefaultParameterValue(0)] int offset) {
  636. return GetStructFromCache(context, fmt).unpack_from(context, buffer, offset);
  637. }
  638. #endregion
  639. #region Write Helpers
  640. private static void WriteShort(StringBuilder res, bool fLittleEndian, short val) {
  641. if (fLittleEndian) {
  642. res.Append((char)(val & 0xff));
  643. res.Append((char)((val >> 8) & 0xff));
  644. } else {
  645. res.Append((char)((val >> 8) & 0xff));
  646. res.Append((char)(val & 0xff));
  647. }
  648. }
  649. private static void WriteUShort(StringBuilder res, bool fLittleEndian, ushort val) {
  650. if (fLittleEndian) {
  651. res.Append((char)(val & 0xff));
  652. res.Append((char)((val >> 8) & 0xff));
  653. } else {
  654. res.Append((char)((val >> 8) & 0xff));
  655. res.Append((char)(val & 0xff));
  656. }
  657. }
  658. private static void WriteInt(StringBuilder res, bool fLittleEndian, int val) {
  659. if (fLittleEndian) {
  660. res.Append((char)(val & 0xff));
  661. res.Append((char)((val >> 8) & 0xff));
  662. res.Append((char)((val >> 16) & 0xff));
  663. res.Append((char)((val >> 24) & 0xff));
  664. } else {
  665. res.Append((char)((val >> 24) & 0xff));
  666. res.Append((char)((val >> 16) & 0xff));
  667. res.Append((char)((val >> 8) & 0xff));
  668. res.Append((char)(val & 0xff));
  669. }
  670. }
  671. private static void WriteUInt(StringBuilder res, bool fLittleEndian, uint val) {
  672. if (fLittleEndian) {
  673. res.Append((char)(val & 0xff));
  674. res.Append((char)((val >> 8) & 0xff));
  675. res.Append((char)((val >> 16) & 0xff));
  676. res.Append((char)((val >> 24) & 0xff));
  677. } else {
  678. res.Append((char)((val >> 24) & 0xff));
  679. res.Append((char)((val >> 16) & 0xff));
  680. res.Append((char)((val >> 8) & 0xff));
  681. res.Append((char)(val & 0xff));
  682. }
  683. }
  684. private static void WritePointer(StringBuilder res, bool fLittleEndian, IntPtr val) {
  685. if (IntPtr.Size == 4) {
  686. WriteInt(res, fLittleEndian, val.ToInt32());
  687. } else {
  688. WriteLong(res, fLittleEndian, val.ToInt64());
  689. }
  690. }
  691. private static void WriteFloat(StringBuilder res, bool fLittleEndian, float val) {
  692. byte[] bytes = BitConverter.GetBytes(val);
  693. if (fLittleEndian) {
  694. res.Append((char)bytes[0]);
  695. res.Append((char)bytes[1]);
  696. res.Append((char)bytes[2]);
  697. res.Append((char)bytes[3]);
  698. } else {
  699. res.Append((char)bytes[3]);
  700. res.Append((char)bytes[2]);
  701. res.Append((char)bytes[1]);
  702. res.Append((char)bytes[0]);
  703. }
  704. }
  705. private static void WriteLong(StringBuilder res, bool fLittleEndian, long val) {
  706. if (fLittleEndian) {
  707. res.Append((char)(val & 0xff));
  708. res.Append((char)((val >> 8) & 0xff));
  709. res.Append((char)((val >> 16) & 0xff));
  710. res.Append((char)((val >> 24) & 0xff));
  711. res.Append((char)((val >> 32) & 0xff));
  712. res.Append((char)((val >> 40) & 0xff));
  713. res.Append((char)((val >> 48) & 0xff));
  714. res.Append((char)((val >> 56) & 0xff));
  715. } else {
  716. res.Append((char)((val >> 56) & 0xff));
  717. res.Append((char)((val >> 48) & 0xff));
  718. res.Append((char)((val >> 40) & 0xff));
  719. res.Append((char)((val >> 32) & 0xff));
  720. res.Append((char)((val >> 24) & 0xff));
  721. res.Append((char)((val >> 16) & 0xff));
  722. res.Append((char)((val >> 8) & 0xff));
  723. res.Append((char)(val & 0xff));
  724. }
  725. }
  726. private static void WriteULong(StringBuilder res, bool fLittleEndian, ulong val) {
  727. if (fLittleEndian) {
  728. res.Append((char)(val & 0xff));
  729. res.Append((char)((val >> 8) & 0xff));
  730. res.Append((char)((val >> 16) & 0xff));
  731. res.Append((char)((val >> 24) & 0xff));
  732. res.Append((char)((val >> 32) & 0xff));
  733. res.Append((char)((val >> 40) & 0xff));
  734. res.Append((char)((val >> 48) & 0xff));
  735. res.Append((char)((val >> 56) & 0xff));
  736. } else {
  737. res.Append((char)((val >> 56) & 0xff));
  738. res.Append((char)((val >> 48) & 0xff));
  739. res.Append((char)((val >> 40) & 0xff));
  740. res.Append((char)((val >> 32) & 0xff));
  741. res.Append((char)((val >> 24) & 0xff));
  742. res.Append((char)((val >> 16) & 0xff));
  743. res.Append((char)((val >> 8) & 0xff));
  744. res.Append((char)(val & 0xff));
  745. }
  746. }
  747. private static void WriteDouble(StringBuilder res, bool fLittleEndian, double val) {
  748. byte[] bytes = BitConverter.GetBytes(val);
  749. if (fLittleEndian) {
  750. res.Append((char)bytes[0]);
  751. res.Append((char)bytes[1]);
  752. res.Append((char)bytes[2]);
  753. res.Append((char)bytes[3]);
  754. res.Append((char)bytes[4]);
  755. res.Append((char)bytes[5]);
  756. res.Append((char)bytes[6]);
  757. res.Append((char)bytes[7]);
  758. } else {
  759. res.Append((char)bytes[7]);
  760. res.Append((char)bytes[6]);
  761. res.Append((char)bytes[5]);
  762. res.Append((char)bytes[4]);
  763. res.Append((char)bytes[3]);
  764. res.Append((char)bytes[2]);
  765. res.Append((char)bytes[1]);
  766. res.Append((char)bytes[0]);
  767. }
  768. }
  769. private static void WriteString(StringBuilder res, int len, string val) {
  770. for (int i = 0; i < val.Length && i < len; i++) {
  771. res.Append(val[i]);
  772. }
  773. for (int i = val.Length; i < len; i++) {
  774. res.Append('\0');
  775. }
  776. }
  777. private static void WritePascalString(StringBuilder res, int len, string val) {
  778. int lenByte = Math.Min(255, Math.Min(val.Length, len));
  779. res.Append((char)lenByte);
  780. for (int i = 0; i < val.Length && i < len; i++) {
  781. res.Append(val[i]);
  782. }
  783. for (int i = val.Length; i < len; i++) {
  784. res.Append('\0');
  785. }
  786. }
  787. #endregion
  788. #region Data getter helpers
  789. internal static bool GetBoolValue(CodeContext/*!*/ context, int index, object[] args) {
  790. object val = GetValue(context, index, args);
  791. object res;
  792. if (Converter.TryConvert(val, typeof(bool), out res)) {
  793. return (bool)res;
  794. }
  795. // Should never happen
  796. throw Error(context, "expected bool value got " + val.ToString());
  797. }
  798. internal static char GetCharValue(CodeContext/*!*/ context, int index, object[] args) {
  799. object val = GetValue(context, index, args);
  800. string strVal = val as string;
  801. if (strVal != null && strVal.Length == 1) return strVal[0];
  802. IList<Byte> byteVal = val as IList<Byte>;
  803. if (byteVal == null || byteVal.Count != 1) throw Error(context, "char format requires string of length 1");
  804. else return (char)byteVal[0];
  805. }
  806. internal static sbyte GetSByteValue(CodeContext/*!*/ context, int index, object[] args) {
  807. object val = GetValue(context, index, args);
  808. sbyte res;
  809. if (Converter.TryConvertToSByte(val, out res)) {
  810. return res;
  811. }
  812. throw Error(context, "expected sbyte value got " + val.ToString());
  813. }
  814. internal static byte GetByteValue(CodeContext/*!*/ context, int index, object[] args) {
  815. object val = GetValue(context, index, args);
  816. byte res;
  817. if (Converter.TryConvertToByte(val, out res)) return res;
  818. char cres;
  819. if (Converter.TryConvertToChar(val, out cres)) return (byte)cres;
  820. throw Error(context, "expected byte value got " + val.ToString());
  821. }
  822. internal static short GetShortValue(CodeContext/*!*/ context, int index, object[] args) {
  823. object val = GetValue(context, index, args);
  824. short res;
  825. if (Converter.TryConvertToInt16(val, out res)) return res;
  826. throw Error(context, "expected short value");
  827. }
  828. internal static ushort GetUShortValue(CodeContext/*!*/ context, int index, object[] args) {
  829. object val = GetValue(context, index, args);
  830. ushort res;
  831. if (Converter.TryConvertToUInt16(val, out res)) return res;
  832. throw Error(context, "expected ushort value");
  833. }
  834. internal static int GetIntValue(CodeContext/*!*/ context, int index, object[] args) {
  835. object val = GetValue(context, index, args);
  836. int res;
  837. if (Converter.TryConvertToInt32(val, out res)) return res;
  838. throw Error(context, "expected int value");
  839. }
  840. internal static uint GetULongValue(CodeContext/*!*/ context, int index, object[] args, string type) {
  841. object val = GetValue(context, index, args);
  842. if (val is int) {
  843. CheckRange(context, (int)val, type);
  844. return (uint)(int)val;
  845. } else if (val is BigInteger) {
  846. CheckRange(context, (BigInteger)val, type);
  847. return (uint)(BigInteger)val;
  848. } else if (val is Extensible<int>) {
  849. CheckRange(context, ((Extensible<int>)val).Value, type);
  850. return (uint)((Extensible<int>)val).Value;
  851. } else if (val is Extensible<BigInteger>) {
  852. CheckRange(context, ((Extensible<BigInteger>)val).Value, type);
  853. return (uint)((Extensible<BigInteger>)val).Value;
  854. } else {
  855. object objres;
  856. if (PythonTypeOps.TryInvokeUnaryOperator(DefaultContext.Default, val, "__int__", out objres)) {
  857. if (objres is int) {
  858. CheckRange(context, (int)objres, type);
  859. return (uint)(int)objres;
  860. }
  861. }
  862. uint res;
  863. if (Converter.TryConvertToUInt32(val, out res)) {
  864. return res;
  865. }
  866. }
  867. throw Error(context, "cannot convert argument to integer");
  868. }
  869. private static void CheckRange(CodeContext context, int val, string type) {
  870. if (val < 0) {
  871. OutOfRange(context, type);
  872. }
  873. }
  874. private static void CheckRange(CodeContext context, BigInteger bi, string type) {
  875. if (bi < 0 || bi > 4294967295) {
  876. OutOfRange(context, type);
  877. }
  878. }
  879. private static void OutOfRange(CodeContext context, string type) {
  880. throw Error(context, string.Format("integer out of range for '{0}' format code", type == "unsigned long" ? "L" : "I"));
  881. }
  882. internal static IntPtr GetPointer(CodeContext/*!*/ context, int index, object[] args) {
  883. object val = GetValue(context, index, args);
  884. if (IntPtr.Size == 4) {
  885. uint res;
  886. if (Converter.TryConvertToUInt32(val, out res)) {
  887. return new IntPtr(res);
  888. }
  889. } else {
  890. long res;
  891. if (Converter.TryConvertToInt64(val, out res)) {
  892. return new IntPtr(res);
  893. }
  894. }
  895. throw Error(context, "expected pointer value");
  896. }
  897. internal static long GetLongValue(CodeContext/*!*/ context, int index, object[] args) {
  898. object val = GetValue(context, index, args);
  899. long res;
  900. if (Converter.TryConvertToInt64(val, out res)) return res;
  901. throw Error(context, "expected long value");
  902. }
  903. internal static ulong GetULongLongValue(CodeContext/*!*/ context, int index, object[] args) {
  904. object val = GetValue(context, index, args);
  905. ulong res;
  906. if (Converter.TryConvertToUInt64(val, out res)) return res;
  907. throw Error(context, "expected ulong value");
  908. }
  909. internal static double GetDoubleValue(CodeContext/*!*/ context, int index, object[] args) {
  910. object val = GetValue(context, index, args);
  911. double res;
  912. if (Converter.TryConvertToDouble(val, out res)) return res;
  913. throw Error(context, "expected double value");
  914. }
  915. internal static string GetStringValue(CodeContext/*!*/ context, int index, object[] args) {
  916. object val = GetValue(context, index, args);
  917. string res;
  918. if (Converter.TryConvertToString(val, out res)) return res;
  919. throw Error(context, "expected string value");
  920. }
  921. internal static object GetValue(CodeContext/*!*/ context, int index, object[] args) {
  922. if (index >= args.Length) throw Error(context, "not enough arguments");
  923. return args[index];
  924. }
  925. #endregion
  926. #region Data creater helpers
  927. internal static bool CreateBoolValue(CodeContext/*!*/ context, ref int index, string data) {
  928. return (int)ReadData(context, ref index, data) != 0;
  929. }
  930. internal static char CreateCharValue(CodeContext/*!*/ context, ref int index, string data) {
  931. return ReadData(context, ref index, data);
  932. }
  933. internal static short CreateShortValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
  934. byte b1 = (byte)ReadData(context, ref index, data);
  935. byte b2 = (byte)ReadData(context, ref index, data);
  936. if (fLittleEndian) {
  937. return (short)((b2 << 8) | b1);
  938. } else {
  939. return (short)((b1 << 8) | b2);
  940. }
  941. }
  942. internal static ushort CreateUShortValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
  943. byte b1 = (byte)ReadData(context, ref index, data);
  944. byte b2 = (byte)ReadData(context, ref index, data);
  945. if (fLittleEndian) {
  946. return (ushort)((b2 << 8) | b1);
  947. } else {
  948. return (ushort)((b1 << 8) | b2);
  949. }
  950. }
  951. internal static float CreateFloatValue(CodeContext/*!*/ context, ref int index, bool fLittleEndian, string data) {
  952. byte[] bytes = new byte[4];
  953. if (fLittleEndian) {
  954. bytes[0] = (byte)ReadData(context, ref index, data);
  955. bytes[1] = (byte)ReadData(context, ref index, data);

Large files files are truncated, but you can click here to view the full file