Files
headphones/mutagen/_id3util.py

177 lines
4.3 KiB
Python

# Copyright (C) 2005 Michael Urman
# 2013 Christoph Reiter
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
class error(Exception):
pass
class ID3NoHeaderError(error, ValueError):
pass
class ID3BadUnsynchData(error, ValueError):
pass
class ID3BadCompressedData(error, ValueError):
pass
class ID3TagError(error, ValueError):
pass
class ID3UnsupportedVersionError(error, NotImplementedError):
pass
class ID3EncryptionUnsupportedError(error, NotImplementedError):
pass
class ID3JunkFrameError(error, ValueError):
pass
class ID3Warning(error, UserWarning):
pass
class unsynch(object):
@staticmethod
def decode(value):
output = []
safe = True
append = output.append
for val in value:
if safe:
append(val)
safe = val != '\xFF'
else:
if val >= '\xE0':
raise ValueError('invalid sync-safe string')
elif val != '\x00':
append(val)
safe = True
if not safe:
raise ValueError('string ended unsafe')
return ''.join(output)
@staticmethod
def encode(value):
output = []
safe = True
append = output.append
for val in value:
if safe:
append(val)
if val == '\xFF':
safe = False
elif val == '\x00' or val >= '\xE0':
append('\x00')
append(val)
safe = val != '\xFF'
else:
append(val)
safe = True
if not safe:
append('\x00')
return ''.join(output)
class _BitPaddedMixin(object):
def as_str(self, width=4, minwidth=4):
return self.to_str(self, self.bits, self.bigendian, width, minwidth)
@staticmethod
def to_str(value, bits=7, bigendian=True, width=4, minwidth=4):
mask = (1 << bits) - 1
if width != -1:
index = 0
bytes_ = bytearray(width)
try:
while value:
bytes_[index] = value & mask
value >>= bits
index += 1
except IndexError:
raise ValueError('Value too wide (>%d bytes)' % width)
else:
# PCNT and POPM use growing integers
# of at least 4 bytes (=minwidth) as counters.
bytes_ = bytearray()
append = bytes_.append
while value:
append(value & mask)
value >>= bits
bytes_ = bytes_.ljust(minwidth, "\x00")
if bigendian:
bytes_.reverse()
return str(bytes_)
@staticmethod
def has_valid_padding(value, bits=7):
"""Whether the padding bits are all zero"""
assert bits <= 8
mask = (((1 << (8 - bits)) - 1) << bits)
if isinstance(value, (int, long)):
while value:
if value & mask:
return False
value >>= 8
elif isinstance(value, str):
for byte in value:
if ord(byte) & mask:
return False
else:
raise TypeError
return True
class BitPaddedInt(int, _BitPaddedMixin):
def __new__(cls, value, bits=7, bigendian=True):
mask = (1 << (bits)) - 1
numeric_value = 0
shift = 0
if isinstance(value, (int, long)):
while value:
numeric_value += (value & mask) << shift
value >>= 8
shift += bits
elif isinstance(value, str):
if bigendian:
value = reversed(value)
for byte in value:
numeric_value += (ord(byte) & mask) << shift
shift += bits
else:
raise TypeError
if isinstance(numeric_value, long):
self = long.__new__(BitPaddedLong, numeric_value)
else:
self = int.__new__(BitPaddedInt, numeric_value)
self.bits = bits
self.bigendian = bigendian
return self
class BitPaddedLong(long, _BitPaddedMixin):
pass