Skip to content

A zero-copy file-like wrapper for Python byte buffers, inspired by Rust's std::io::Cursor.

License

Notifications You must be signed in to change notification settings

althonos/iocursor

Repository files navigation

io‸cursor

A zero-copy file-like wrapper for Python byte buffers, inspired by Rust's std::io::Cursor.

Actions Coverage PyPI Wheel Python Versions Python Implementations License Source GitHub issues Downloads Changelog

🗺️ Overview

iocursor.Cursor lets you wrap an allocated buffer (i.e. a Python object implementing the buffer protocol), and interfacing with it through the API of a file-like object. It shares some common points with io.BytesIO but with the following main differences:

  • zero-copy VS copy: Cursor will not copy the data you give it at initialisation, while BytesIO will. This makes Cursor more efficient when you are using it for read-only operations.
  • static VS growable: Cursor will only use the buffer you give it at static memory, while BytesIO will use its dedicated, growable buffer.

🔧 Installing

Install directly from PyPI, using pip:

$ pip install iocursor

Pre-built wheels are available on Linux and OSX for all supported Python3 versions. Otherwise, building from source only requires a working C compiler.

🧶 Thread-safety

iocursor.Cursor instances are not thread-safe. Using several Cursor instances with the same backend memory only for reading should be fine. Use a lock when interfacing otherwise.

💡 Examples

  • Use iocursor.Cursor when you have bytes you need to pass to an interface that only accepts file-like objects. For instance, pass a PNG image decoded from base64 to PIL, without copy:
    import base64
    from iocursor import Cursor
    from PIL import Image
    
    imgdata = base64.b64decode("iVBORw0KGgoAAAANSUhEU...")
    img = Image.open(Cursor(imgdata))
  • Use iocursor.Cursor when you want to use the file-like API to write to a buffer of known size. For instance, retrieve a file using the pysmb API, which only accepts file-like objects:
    from SMB.SMBConnection import SMBConnectSMBConnection
    
    smb = SMBConnection('guest', '', 'client', 'server')
    smb.connect("192.168.0.1")
    
    info = smb.getAttributes("Music", "The Clash/Rock the Casbah.mp3")
    cursor = Cursor(bytearray(shared_file.file_size))
    smb.retrieveFile("Music", "The Clash/Rock the Casbah.mp3", cursor)
    
    buffer = cursor.getvalue()
  • Use iocursor.Cursor when you want to do direct I/O on a type implementing the buffer protocol. For instance, initialize a numpy array by writing bytes to it:
    import numpy
    
    array = numpy.empty(4, dtype="int16")
    cursor = Cursor(array)
    cursor.write(b"\x01\x00\x02\x00\x03\x00\x04\x00")
    print(array)  # array([1, 2, 3, 4], dtype=int16)

💭 Feedback

⚠️ Issue Tracker

Found a bug ? Have an enhancement request ? Head over to the GitHub issue tracker if you need to report or ask something. If you are filing in on a bug, please include as much information as you can about the issue, and try to recreate the same bug in a simple, easily reproducible situation.

🏗️ Contributing

Contributions are more than welcome! See CONTRIBUTING.md for more details.

⚖️ License

This library is provided under the MIT License.