PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Languages/IronPython/IronPython.Modules/_bytesio.cs

http://github.com/IronLanguages/main
C# | 553 lines | 403 code | 104 blank | 46 comment | 66 complexity | 506dd2cdadc3fbeab243cdab7cc75879 MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  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. * dlr@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. #if FEATURE_CORE_DLR
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. #if FEATURE_NUMERICS
  21. using System.Numerics;
  22. #else
  23. using Microsoft.Scripting.Math;
  24. #endif
  25. using System;
  26. using System.Collections;
  27. using System.Collections.Generic;
  28. using System.Diagnostics;
  29. using System.Dynamic;
  30. using System.IO;
  31. using System.Runtime.InteropServices;
  32. using System.Text;
  33. using Microsoft.Scripting.Runtime;
  34. using Microsoft.Scripting.Utils;
  35. using IronPython.Runtime;
  36. using IronPython.Runtime.Binding;
  37. using IronPython.Runtime.Operations;
  38. using IronPython.Runtime.Types;
  39. using IronPython.Runtime.Exceptions;
  40. namespace IronPython.Modules {
  41. public static partial class PythonIOModule {
  42. /// <summary>
  43. /// BytesIO([initializer]) -> object
  44. ///
  45. /// Create a buffered I/O implementation using an in-memory bytes
  46. /// buffer, ready for reading and writing.
  47. /// </summary>
  48. [PythonType, DontMapIDisposableToContextManager]
  49. public class BytesIO : _BufferedIOBase, IEnumerator, IDisposable, IDynamicMetaObjectProvider {
  50. #region Fields and constructors
  51. private static readonly int DEFAULT_BUF_SIZE = 20;
  52. private byte[] _data;
  53. private int _pos, _length;
  54. internal BytesIO(CodeContext/*!*/ context)
  55. : base(context) {
  56. }
  57. public BytesIO(CodeContext/*!*/ context, [DefaultParameterValue(null)]object initial_bytes)
  58. : base(context) {
  59. }
  60. public void __init__([DefaultParameterValue(null)]object initial_bytes) {
  61. if (Object.ReferenceEquals(_data, null)) {
  62. _data = new byte[DEFAULT_BUF_SIZE];
  63. }
  64. _pos = _length = 0;
  65. if (initial_bytes != null) {
  66. DoWrite(initial_bytes);
  67. _pos = 0;
  68. }
  69. }
  70. #endregion
  71. #region Public API
  72. /// <summary>
  73. /// close() -> None. Disable all I/O operations.
  74. /// </summary>
  75. public override void close(CodeContext/*!*/ context) {
  76. _data = null;
  77. }
  78. /// <summary>
  79. /// True if the file is closed.
  80. /// </summary>
  81. public override bool closed {
  82. get {
  83. return _data == null;
  84. }
  85. }
  86. /// <summary>
  87. /// getvalue() -> bytes.
  88. ///
  89. /// Retrieve the entire contents of the BytesIO object.
  90. /// </summary>
  91. public Bytes getvalue() {
  92. _checkClosed();
  93. if (_length == 0) {
  94. return Bytes.Empty;
  95. }
  96. byte[] arr = new byte[_length];
  97. Array.Copy(_data, arr, _length);
  98. return Bytes.Make(arr);
  99. }
  100. [Documentation("isatty() -> False\n\n"
  101. + "Always returns False since BytesIO objects are not connected\n"
  102. + "to a TTY-like device."
  103. )]
  104. public override bool isatty(CodeContext/*!*/ context) {
  105. _checkClosed();
  106. return false;
  107. }
  108. [Documentation("read([size]) -> read at most size bytes, returned as a bytes object.\n\n"
  109. + "If the size argument is negative, read until EOF is reached.\n"
  110. + "Return an empty string at EOF."
  111. )]
  112. public override object read(CodeContext/*!*/ context, [DefaultParameterValue(null)]object size) {
  113. _checkClosed();
  114. int sz = GetInt(size, -1);
  115. int len = Math.Max(0, _length - _pos);
  116. if (sz >= 0) {
  117. len = Math.Min(len, sz);
  118. }
  119. if (len == 0) {
  120. return Bytes.Empty;
  121. }
  122. byte[] arr = new byte[len];
  123. Array.Copy(_data, _pos, arr, 0, len);
  124. _pos += len;
  125. return Bytes.Make(arr);
  126. }
  127. [Documentation("read1(size) -> read at most size bytes, returned as a bytes object.\n\n"
  128. + "If the size argument is negative or omitted, read until EOF is reached.\n"
  129. + "Return an empty string at EOF."
  130. )]
  131. public override Bytes read1(CodeContext/*!*/ context, int size) {
  132. return (Bytes)read(context, size);
  133. }
  134. public override bool readable(CodeContext/*!*/ context) {
  135. return true;
  136. }
  137. [Documentation("readinto(array_or_bytearray) -> int. Read up to len(b) bytes into b.\n\n"
  138. + "Returns number of bytes read (0 for EOF)."
  139. )]
  140. public BigInteger readinto([NotNull]ByteArray buffer) {
  141. _checkClosed();
  142. int len = Math.Min(_length - _pos, buffer.Count);
  143. for (int i = 0; i < len; i++) {
  144. buffer[i] = _data[_pos++];
  145. }
  146. return len;
  147. }
  148. public BigInteger readinto([NotNull]ArrayModule.array buffer) {
  149. _checkClosed();
  150. int len = Math.Min(_length - _pos, buffer.__len__() * buffer.itemsize);
  151. int tailLen = len % buffer.itemsize;
  152. buffer.FromStream(new MemoryStream(_data, _pos, len - tailLen, false, false), 0);
  153. _pos += len - tailLen;
  154. if (tailLen != 0) {
  155. byte[] tail = buffer.RawGetItem(len / buffer.itemsize);
  156. for (int i = 0; i < tailLen; i++) {
  157. tail[i] = _data[_pos++];
  158. }
  159. buffer.FromStream(new MemoryStream(tail), len / buffer.itemsize);
  160. }
  161. return len;
  162. }
  163. public override BigInteger readinto(CodeContext/*!*/ context, object buf) {
  164. ByteArray bytes = buf as ByteArray;
  165. if (bytes != null) {
  166. return readinto(bytes);
  167. }
  168. ArrayModule.array array = buf as ArrayModule.array;
  169. if (array != null) {
  170. return readinto(array);
  171. }
  172. _checkClosed();
  173. throw PythonOps.TypeError("must be read-write buffer, not {0}", PythonTypeOps.GetName(buf));
  174. }
  175. [Documentation("readline([size]) -> next line from the file, as bytes.\n\n"
  176. + "Retain newline. A non-negative size argument limits the maximum\n"
  177. + "number of bytes to return (an incomplete line may be returned then).\n"
  178. + "Return an empty string at EOF."
  179. )]
  180. public override object readline(CodeContext/*!*/ context, [DefaultParameterValue(-1)]int limit) {
  181. return readline(limit);
  182. }
  183. private Bytes readline([DefaultParameterValue(-1)]int size) {
  184. _checkClosed();
  185. if (_pos >= _length || size == 0) {
  186. return Bytes.Empty;
  187. }
  188. int origPos = _pos;
  189. while ((size < 0 || _pos - origPos < size) && _pos < _length) {
  190. if (_data[_pos] == '\n') {
  191. _pos++;
  192. break;
  193. }
  194. _pos++;
  195. }
  196. byte[] arr = new byte[_pos - origPos];
  197. Array.Copy(_data, origPos, arr, 0, _pos - origPos);
  198. return Bytes.Make(arr);
  199. }
  200. public Bytes readline(object size) {
  201. if (size == null) {
  202. return readline(-1);
  203. }
  204. _checkClosed();
  205. throw PythonOps.TypeError("integer argument expected, got '{0}'", PythonTypeOps.GetName(size));
  206. }
  207. [Documentation("readlines([size]) -> list of bytes objects, each a line from the file.\n\n"
  208. + "Call readline() repeatedly and return a list of the lines so read.\n"
  209. + "The optional size argument, if given, is an approximate bound on the\n"
  210. + "total number of bytes in the lines returned."
  211. )]
  212. public override List readlines([DefaultParameterValue(null)]object hint) {
  213. _checkClosed();
  214. int size = GetInt(hint, -1);
  215. List lines = new List();
  216. for (Bytes line = readline(-1); line.Count > 0; line = readline(-1)) {
  217. lines.append(line);
  218. if (size > 0) {
  219. size -= line.Count;
  220. if (size <= 0) {
  221. break;
  222. }
  223. }
  224. }
  225. return lines;
  226. }
  227. [Documentation("seek(pos, whence=0) -> int. Change stream position.\n\n"
  228. + "Seek to byte offset pos relative to position indicated by whence:\n"
  229. + " 0 Start of stream (the default). pos should be >= 0;\n"
  230. + " 1 Current position - pos may be negative;\n"
  231. + " 2 End of stream - pos usually negative.\n"
  232. + "Returns the new absolute position."
  233. )]
  234. public BigInteger seek(int pos, [DefaultParameterValue(0)]int whence) {
  235. _checkClosed();
  236. switch (whence) {
  237. case 0:
  238. if (pos < 0) {
  239. throw PythonOps.ValueError("negative seek value {0}", pos);
  240. }
  241. _pos = pos;
  242. return _pos;
  243. case 1:
  244. _pos = Math.Max(0, _pos + pos);
  245. return _pos;
  246. case 2:
  247. _pos = Math.Max(0, _length + pos);
  248. return _pos;
  249. default:
  250. throw PythonOps.ValueError("invalid whence ({0}, should be 0, 1 or 2)", whence);
  251. }
  252. }
  253. public BigInteger seek(double pos, [DefaultParameterValue(0)]int whence) {
  254. throw PythonOps.TypeError("'float' object cannot be interpreted as an index");
  255. }
  256. public override BigInteger seek(CodeContext/*!*/ context, BigInteger pos, [DefaultParameterValue(0)]object whence) {
  257. _checkClosed();
  258. int posInt = (int)pos;
  259. if (whence is double || whence is Extensible<double>) {
  260. if (PythonContext.GetContext(context).PythonOptions.Python30) {
  261. throw PythonOps.TypeError("integer argument expected, got float");
  262. } else {
  263. PythonOps.Warn(context, PythonExceptions.DeprecationWarning, "integer argument expected, got float");
  264. return seek(posInt, Converter.ConvertToInt32(whence));
  265. }
  266. }
  267. return seek(posInt, GetInt(whence));
  268. }
  269. public override bool seekable(CodeContext/*!*/ context) {
  270. return true;
  271. }
  272. [Documentation("tell() -> current file position, an integer")]
  273. public override BigInteger tell(CodeContext/*!*/ context) {
  274. _checkClosed();
  275. return _pos;
  276. }
  277. [Documentation("truncate([size]) -> int. Truncate the file to at most size bytes.\n\n"
  278. + "Size defaults to the current file position, as returned by tell().\n"
  279. + "Returns the new size. Imply an absolute seek to the position size."
  280. )]
  281. public BigInteger truncate() {
  282. return truncate(_pos);
  283. }
  284. public BigInteger truncate(int size) {
  285. _checkClosed();
  286. if (size < 0) {
  287. throw PythonOps.ValueError("negative size value {0}", size);
  288. }
  289. _length = Math.Min(_length, size);
  290. return (BigInteger)size;
  291. }
  292. public override BigInteger truncate(CodeContext/*!*/ context, [DefaultParameterValue(null)]object size) {
  293. if (size == null) {
  294. return truncate();
  295. }
  296. int sizeInt;
  297. if (TryGetInt(size, out sizeInt)) {
  298. return truncate(sizeInt);
  299. }
  300. _checkClosed();
  301. throw PythonOps.TypeError("integer argument expected, got '{0}'", PythonTypeOps.GetName(size));
  302. }
  303. public override bool writable(CodeContext/*!*/ context) {
  304. return true;
  305. }
  306. [Documentation("write(bytes) -> int. Write bytes to file.\n\n"
  307. + "Return the number of bytes written."
  308. )]
  309. public override BigInteger write(CodeContext/*!*/ context, object bytes) {
  310. _checkClosed();
  311. return DoWrite(bytes);
  312. }
  313. [Documentation("writelines(sequence_of_strings) -> None. Write strings to the file.\n\n"
  314. + "Note that newlines are not added. The sequence can be any iterable\n"
  315. + "object producing strings. This is equivalent to calling write() for\n"
  316. + "each string."
  317. )]
  318. public void writelines([NotNull]IEnumerable lines) {
  319. _checkClosed();
  320. IEnumerator en = lines.GetEnumerator();
  321. while (en.MoveNext()) {
  322. DoWrite(en.Current);
  323. }
  324. }
  325. #endregion
  326. #region IDisposable methods
  327. void IDisposable.Dispose() { }
  328. #endregion
  329. #region IEnumerator methods
  330. private object _current = null;
  331. object IEnumerator.Current {
  332. get {
  333. _checkClosed();
  334. return _current;
  335. }
  336. }
  337. bool IEnumerator.MoveNext() {
  338. Bytes line = readline(-1);
  339. if (line.Count == 0) {
  340. return false;
  341. }
  342. _current = line;
  343. return true;
  344. }
  345. void IEnumerator.Reset() {
  346. seek(0, 0);
  347. _current = null;
  348. }
  349. #endregion
  350. #region IDynamicMetaObjectProvider Members
  351. DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
  352. return new MetaExpandable<BytesIO>(parameter, this);
  353. }
  354. #endregion
  355. #region Private implementation details
  356. private int DoWrite(byte[] bytes) {
  357. if (bytes.Length == 0) {
  358. return 0;
  359. }
  360. EnsureSizeSetLength(_pos + bytes.Length);
  361. Array.Copy(bytes, 0, _data, _pos, bytes.Length);
  362. _pos += bytes.Length;
  363. return bytes.Length;
  364. }
  365. private int DoWrite(ICollection<byte> bytes) {
  366. int nbytes = bytes.Count;
  367. if (nbytes == 0) {
  368. return 0;
  369. }
  370. EnsureSizeSetLength(_pos + nbytes);
  371. bytes.CopyTo(_data, _pos);
  372. _pos += nbytes;
  373. return nbytes;
  374. }
  375. private int DoWrite(string bytes) {
  376. // CLR strings are natively Unicode (UTF-16 LE, to be precise).
  377. // In 2.x, io.BytesIO.write() takes "bytes or bytearray" as a parameter.
  378. // On 2.x "bytes" is an alias for str, so str types are accepted by BytesIO.write().
  379. // When given a unicode object, 2.x BytesIO.write() complains:
  380. // TypeError: 'unicode' does not have the buffer interface
  381. // We will accept CLR strings, but only if the data in it is in Latin 1 (iso-8859-1)
  382. // encoding (i.e. ord(c) for all c is within 0-255.
  383. // Alternatively, we could support strings containing any Unicode character by ignoring
  384. // any 0x00 bytes, but as CPython doesn't support that it is unlikely that we will need to.
  385. int nbytes = bytes.Length;
  386. if (nbytes == 0) {
  387. return 0;
  388. }
  389. byte[] _raw_string = new byte[nbytes];
  390. for (int i = 0; i < nbytes; i++) {
  391. int ord = (int)bytes[i];
  392. if(ord < 256) {
  393. _raw_string[i] = (byte)ord;
  394. } else {
  395. // A character outside the range 0x00-0xFF is present in the original string.
  396. // Ejecting, emulating the cPython 2.x behavior when it enounters "unicode".
  397. // This should keep the unittest gods at bay.
  398. throw PythonOps.TypeError("'unicode' does not have the buffer interface");
  399. }
  400. }
  401. return DoWrite(_raw_string);
  402. }
  403. private int DoWrite(object bytes) {
  404. if (bytes is byte[]) {
  405. return DoWrite((byte[])bytes);
  406. } else if (bytes is Bytes) {
  407. return DoWrite(((Bytes)bytes)._bytes); // as byte[]
  408. } else if (bytes is ArrayModule.array) {
  409. return DoWrite(((ArrayModule.array)bytes).ToByteArray()); // as byte[]
  410. } else if (bytes is ICollection<byte>) {
  411. return DoWrite((ICollection<byte>)bytes);
  412. } else if (bytes is string) {
  413. // TODO Remove this when we move to 3.x
  414. return DoWrite((string)bytes); // as string
  415. }
  416. throw PythonOps.TypeError("expected a readable buffer object");
  417. }
  418. private void EnsureSize(int size) {
  419. Debug.Assert(size > 0);
  420. if (_data.Length < size) {
  421. if (size <= DEFAULT_BUF_SIZE) {
  422. size = DEFAULT_BUF_SIZE;
  423. } else {
  424. size = Math.Max(size, _data.Length * 2);
  425. }
  426. byte[] oldBuffer = _data;
  427. _data = new byte[size];
  428. Array.Copy(oldBuffer, _data, _length);
  429. }
  430. }
  431. private void EnsureSizeSetLength(int size) {
  432. Debug.Assert(size >= _pos);
  433. Debug.Assert(_length <= _data.Length);
  434. if (_data.Length < size) {
  435. // EnsureSize is guaranteed to resize, so we need not write any zeros here.
  436. EnsureSize(size);
  437. _length = size;
  438. return;
  439. }
  440. // _data[_pos:size] is about to be overwritten, so we only need to zero out _data[_length:_pos]
  441. while (_length < _pos) {
  442. _data[_length++] = 0;
  443. }
  444. _length = Math.Max(_length, size);
  445. }
  446. #endregion
  447. }
  448. }
  449. }