<commit_before># -*- coding: utf-8 -*-
#
# Copyright (c) 2007-2008 The PyAMF Project.
# See LICENSE for details.

"""
Local Shared Object implementation.

Local Shared Object (LSO), sometimes known as flash cookies, is a cookie-like data
entity used by the Flash Player and Gnash. The players allow web content to read
and write LSO data to the computer's local drive on a per-domain basis.

@see: U{Local Shared Object on WikiPedia (external)
<http://en.wikipedia.org/wiki/Local_Shared_Object>}
@see: U{Local Shared Object envelope (external)
<http://osflash.org/documentation/amf/envelopes/sharedobject>}

@author: U{<NAME><mailto:<EMAIL>>}

@since: 0.1.0
"""

import types

import pyamf
from pyamf import util

#: Magic Number - 2 bytes
HEADER_VERSION = '\x00\xbf'
#: Marker - 10 bytes
HEADER_SIGNATURE = 'TCSO\x00\x04\x00\x00\x00\x00'
#: Padding - 4 bytes
PADDING_BYTE = '\x00'

def decode(stream, strict=True):
    """
    Decodes a SOL stream. C{strict} mode ensures that the sol stream is as spec
    compatible as possible.

    @raise DecodeError: One of:
     - Unknown SOL version in header.
     - Inconsistent stream header length.
     - Invalid signature.
     - Invalid padding read.
     - Missing padding byte.

    @return: A C{tuple} containing the C{root_name} and a C{dict} of name, value pairs.
    @rtype: C{tuple}
    """
    if not isinstance(stream, util.BufferedByteStream):
        stream = util.BufferedByteStream(stream)

    # read the version
    version = stream.read(2)

    if version != HEADER_VERSION:
        raise pyamf.DecodeError, 'Unknown SOL version in header'

    # read the length
    length = stream.read_ulong()

    if strict and stream.remaining() != length:
        raise pyamf.DecodeError, 'Inconsistent stream header length'

    # read the signature
    signature = stream.read(10)

    if signature != HEADER_SIGNATURE:
        raise pyamf.DecodeError, 'Invalid signature'

    length = stream.read_ushort()
    root_name = stream.read_utf8_string(length)

    # read padding
    if stream.read(3) != PADDING_BYTE * 3:
        raise pyamf.DecodeError, 'Invalid padding read'

    decoder = pyamf.get_decoder(stream.read_uchar())
    decoder.stream = stream

    values = {}

    while 1:
        if stream.at_eof():
            break

        name = decoder.readString()
        value = decoder.readElement()

        # read the padding
        if stream.read(1) != PADDING_BYTE:
            raise pyamf.DecodeError, 'Missing padding byte'

        values[name] = value

    return (root_name, values)

def encode(name, values, strict=True, encoding=pyamf.AMF0):
    """
    Produces a SharedObject encoded stream based on the name and values.

    @param name: The root name of the SharedObject.
    @type name: C{basestring}
    @param values: A C{dict} of name value pairs to be encoded in the stream.
    @type values: C{dict}
    @return: A SharedObject encoded stream.
    @rtype: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
    """
    encoder = pyamf.get_encoder(encoding)
    encoder.stream = stream = util.BufferedByteStream()

    # write the header
    stream.write(HEADER_VERSION)

    if strict is True:
        length_pos = stream.tell()

    stream.write_ulong(0)

    # write the signature
    stream.write(HEADER_SIGNATURE)

    # write the root name
    if not isinstance(name, unicode):
        name = unicode(name)

    stream.write_ushort(len(name))
    stream.write_utf8_string(name)

    # write the padding
    stream.write(PADDING_BYTE * 3)
    stream.write_uchar(encoding)

    for n, v in values.iteritems():
        encoder._writeString(n)
        encoder.writeElement(v)

        # write the padding
        stream.write(PADDING_BYTE)

    if strict:
        stream.seek(length_pos)
        stream.write_ulong(stream.remaining() - 4)

    stream.seek(0)

    return stream

def load(name_or_file):
    """
    Loads a sol file and returns a L{SOL} object
    """
    f = name_or_file
    opened = False

    if isinstance(name_or_file, basestring):
        f = open(name_or_file, 'rb')
        opened = True
    elif not isinstance(name_or_file, types.FileType):
        raise ValueError, 'file name or file resource expected'

    name, values = decode(f.read())
    s = SOL(name)
    
    for n, v in values.iteritems():
        s[n] = v

    if opened is True:
        f.close()

    return s

def save(sol, name_or_file):
    """
    Writes a L{SOL} object to C{name_or_file}
    """
    f = name_or_file
    opened = False

    if isinstance(name_or_file, basestring):
        f = open(name_or_file, 'wb+')
        opened = True
    elif not isinstance(name_or_file, types.FileType):
        raise ValueError, 'file name or file resource expected'

    f.write(encode(sol.name, sol).getvalue())

    if opened:
        f.close()

class SOL(dict):
    def __init__(self, name):
        self.name = name

    def save(self, name_or_file):
        save(self, name_or_file)

LSO = SOL<commit_msg>Added encoding kwarg to sol.save<commit_after># -*- coding: utf-8 -*-
#
# Copyright (c) 2007-2008 The PyAMF Project.
# See LICENSE for details.

"""
Local Shared Object implementation.

Local Shared Object (LSO), sometimes known as flash cookies, is a cookie-like data
entity used by the Flash Player and Gnash. The players allow web content to read
and write LSO data to the computer's local drive on a per-domain basis.

@see: U{Local Shared Object on WikiPedia (external)
<http://en.wikipedia.org/wiki/Local_Shared_Object>}
@see: U{Local Shared Object envelope (external)
<http://osflash.org/documentation/amf/envelopes/sharedobject>}

@author: U{<NAME><mailto:<EMAIL>>}

@since: 0.1.0
"""

import types

import pyamf
from pyamf import util

#: Magic Number - 2 bytes
HEADER_VERSION = '\x00\xbf'
#: Marker - 10 bytes
HEADER_SIGNATURE = 'TCSO\x00\x04\x00\x00\x00\x00'
#: Padding - 4 bytes
PADDING_BYTE = '\x00'

def decode(stream, strict=True):
    """
    Decodes a SOL stream. C{strict} mode ensures that the sol stream is as spec
    compatible as possible.

    @raise DecodeError: One of:
     - Unknown SOL version in header.
     - Inconsistent stream header length.
     - Invalid signature.
     - Invalid padding read.
     - Missing padding byte.

    @return: A C{tuple} containing the C{root_name} and a C{dict} of name, value pairs.
    @rtype: C{tuple}
    """
    if not isinstance(stream, util.BufferedByteStream):
        stream = util.BufferedByteStream(stream)

    # read the version
    version = stream.read(2)

    if version != HEADER_VERSION:
        raise pyamf.DecodeError, 'Unknown SOL version in header'

    # read the length
    length = stream.read_ulong()

    if strict and stream.remaining() != length:
        raise pyamf.DecodeError, 'Inconsistent stream header length'

    # read the signature
    signature = stream.read(10)

    if signature != HEADER_SIGNATURE:
        raise pyamf.DecodeError, 'Invalid signature'

    length = stream.read_ushort()
    root_name = stream.read_utf8_string(length)

    # read padding
    if stream.read(3) != PADDING_BYTE * 3:
        raise pyamf.DecodeError, 'Invalid padding read'

    decoder = pyamf.get_decoder(stream.read_uchar())
    decoder.stream = stream

    values = {}

    while 1:
        if stream.at_eof():
            break

        name = decoder.readString()
        value = decoder.readElement()

        # read the padding
        if stream.read(1) != PADDING_BYTE:
            raise pyamf.DecodeError, 'Missing padding byte'

        values[name] = value

    return (root_name, values)

def encode(name, values, strict=True, encoding=pyamf.AMF0):
    """
    Produces a SharedObject encoded stream based on the name and values.

    @param name: The root name of the SharedObject.
    @type name: C{basestring}
    @param values: A C{dict} of name value pairs to be encoded in the stream.
    @type values: C{dict}
    @return: A SharedObject encoded stream.
    @rtype: L{BufferedByteStream<pyamf.util.BufferedByteStream>}
    """
    encoder = pyamf.get_encoder(encoding)
    encoder.stream = stream = util.BufferedByteStream()

    # write the header
    stream.write(HEADER_VERSION)

    if strict is True:
        length_pos = stream.tell()

    stream.write_ulong(0)

    # write the signature
    stream.write(HEADER_SIGNATURE)

    # write the root name
    if not isinstance(name, unicode):
        name = unicode(name)

    stream.write_ushort(len(name))
    stream.write_utf8_string(name)

    # write the padding
    stream.write(PADDING_BYTE * 3)
    stream.write_uchar(encoding)

    for n, v in values.iteritems():
        encoder._writeString(n)
        encoder.writeElement(v)

        # write the padding
        stream.write(PADDING_BYTE)

    if strict:
        stream.seek(length_pos)
        stream.write_ulong(stream.remaining() - 4)

    stream.seek(0)

    return stream

def load(name_or_file):
    """
    Loads a sol file and returns a L{SOL} object
    """
    f = name_or_file
    opened = False

    if isinstance(name_or_file, basestring):
        f = open(name_or_file, 'rb')
        opened = True
    elif not isinstance(name_or_file, types.FileType):
        raise ValueError, 'file name or file resource expected'

    name, values = decode(f.read())
    s = SOL(name)
    
    for n, v in values.iteritems():
        s[n] = v

    if opened is True:
        f.close()

    return s

def save(sol, name_or_file, encoding=pyamf.AMF0):
    """
    Writes a L{SOL} object to C{name_or_file}
    """
    f = name_or_file
    opened = False

    if isinstance(name_or_file, basestring):
        f = open(name_or_file, 'wb+')
        opened = True
    elif not isinstance(name_or_file, types.FileType):
        raise ValueError, 'file name or file resource expected'

    f.write(encode(sol.name, sol, encoding=encoding).getvalue())

    if opened:
        f.close()

class SOL(dict):
    def __init__(self, name):
        self.name = name

    def save(self, name_or_file):
        save(self, name_or_file)

LSO = SOL
{'fraction_non_alphanumeric': 0.10096584685850842, 'fraction_numerical': 0.009260181220750771, 'mean_word_length': 2.8292032024399543, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3544118', 'n_tokens_mistral': 3324, 'n_tokens_neox': 3131, 'n_words': 935}