<commit_before>from collections import defaultdict

class ObservableMemory:
    def __init__(self, subject=None):
        if subject is None:
            subject = 0x10000 * [0x00]
        self._subject = subject

        self._listeners = defaultdict(list)
        self._providers = defaultdict(list)        

    def __setitem__(self, address, value):
        callbacks = self._listeners[address]
        for callback in callbacks:
            result = callback(address, value)
            if result is not None:
                value = result

    def __getitem__(self, address):
        callbacks = self._providers[address]
        final_result = None

        for callback in callbacks:
            result = callback(address)
            if result is not None:
                final_result = result

        if final_result:
            return final_result
        else:
            return self._subject[address]
    
    def __getattr__(self, address):
        return getattr(self._subject, address)

    def register_provider(self, address_range, callback):
        for address in address_range:
            self._providers[address].append(callback)

    def register_listener(self, address_range, callback):
        for address in address_range:
            self._listeners[address].append(callback)

    def write(self, start_address, bytes):
        self._subject[start_address:start_address+len(bytes)] = bytes<commit_msg> - Fix __setitem__() to allow callback to change memory.  - Don't allow same callback to be registered twice on an address.<commit_after>from collections import defaultdict

class ObservableMemory:
    def __init__(self, subject=None):
        if subject is None:
            subject = 0x10000 * [0x00]
        self._subject = subject

        self._listeners = defaultdict(list)
        self._providers = defaultdict(list)        

    def __setitem__(self, address, value):
        callbacks = self._listeners[address]
        for callback in callbacks:
            result = callback(address, value)
            if result is not None:
                value = result
        self._subject[address] = value
        
    def __getitem__(self, address):
        callbacks = self._providers[address]
        final_result = None

        for callback in callbacks:
            result = callback(address)
            if result is not None:
                final_result = result

        if final_result is None:
            return self._subject[address]
        else:
            return final_result
    
    def __getattr__(self, attribute):
        return getattr(self._subject, attribute)

    def register_listener(self, address_range, callback):
        for address in address_range:
            if not callback in self._listeners[address]:
                self._listeners[address].append(callback)

    def register_provider(self, address_range, callback): 
        for address in address_range:
            if not callback in self._providers[address]:
                self._providers[address].append(callback)

    def write(self, start_address, bytes):
        self._subject[start_address:start_address+len(bytes)] = bytes
{'fraction_non_alphanumeric': 0.07251424952501584, 'fraction_numerical': 0.005699810006333122, 'mean_word_length': 2.290625, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '612975', 'n_tokens_mistral': 819, 'n_tokens_neox': 783, 'n_words': 230}