# $SnapHashLicense:
#
# SnapLogic - Open source data services
#
# Copyright (C) 2009, SnapLogic, Inc. All rights reserved.
#
# See http://www.snaplogic.org for more information about
# the SnapLogic project.
#
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the LEGAL file
# at the top of the source tree.
#
# "SnapLogic" is a trademark of SnapLogic, Inc.
#
#
# $
# $Id: string_buffer.py 7923 2009-06-19 23:49:04Z dmitri $
"""
A consumable string buffer.
"""
import threading
from snaplogic.common.snap_exceptions import *
class StringBuffer(object):
"""
Provide simple non-blocking file I/O like operations on a string.
This is a very simple implementation of file I/O on a string similar in purpose to StringIO. The difference
here is that the string is actually consumed on reading. Also the read and write pointers remain at the beginning
and end of the string respectively at all times.
"""
def __init__(self):
"""
Initialization.
"""
self._data = ''
self._open = True
self.lock = threading.RLock()
def length(self):
"""
Get the length of the string buffer.
@return: Length of the data stored in buffer.
@rtype: int
"""
self.lock.acquire()
try:
return len(self._data)
finally:
self.lock.release()
def write(self, data):
"""
Write data to the buffer.
@param data: A buffer of bytes to write.
@type data: string
"""
self.lock.acquire()
try:
if not self._open:
raise SnapValueError("Attempt to write to a closed StringBuffer object.")
self._data += data
finally:
self.lock.release()
def read(self, size=None):
"""
Non-blocking read of buffer.
Attempts to read up to size bytes of data out of the buffer. If not enough data is available, it
returns immediately with an empty string. Otherwise returns the amount of data requested. If size is
not given or None, reads whatever data is available at the time of call.
@param size: Number of bytes to read.
@type size: int
@return: A buffer of bytes read. If EOF, None is returned.
@rtype: string
"""
self.lock.acquire()
try:
if not self._open and not self._data:
return None
if size is None:
data = self._data
self._data = ''
else:
# Note that even if the slice size exceeds the bounds of the string, Python seems to handle
# it correctly.
data = self._data[:size]
self._data = self._data[size:]
return data
finally:
self.lock.release()
def close(self):
"""
Close the buffer.
Closes the buffer so that futher writes will result in an error. Reading can continue as normal until
the internal string buffer has been completely exhausted. At this point, read() will consider the
buffer to be in an EOF state.
"""
self.lock.acquire()
try:
self._open = False
finally:
self.lock.release()
|