<commit_before>from rsqueakvm import constants, error
from rsqueakvm.model.base import W_AbstractObjectWithIdentityHash
from rsqueakvm.model.variable import W_WordsObject
from rsqueakvm.util import system

from rpython.rlib import jit
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rlib.rarithmetic import r_uint


def from_words_object(w_obj, form):
    depth = form.depth()
    space = form.space
    size = w_obj.size()
    w_class = w_obj.getclass(space)
    if not w_class.is_same_object(space.w_Bitmap):
        raise error.WrappingError

    if depth < 8:
        w_display_bitmap = W_MappingDisplayBitmap(space, size, depth)
    elif depth == 8:
        w_display_bitmap = W_8BitDisplayBitmap(space, size, depth)
    elif depth == 16:
        w_display_bitmap = W_16BitDisplayBitmap(space, size, depth)
    else:
        assert depth == 32
        w_display_bitmap = W_32BitDisplayBitmap(space, size, depth)

    for idx in range(size):
        w_display_bitmap.setword(idx, w_obj.getword(idx))

    return w_display_bitmap


class W_DisplayBitmap(W_AbstractObjectWithIdentityHash):
    _attrs_ = ['pixelbuffer_words', '_real_depth_buffer', '_realsize', '_display', '_depth']
    _immutable_fields_ = ['pixelbuffer_words?', '_real_depth_buffer', '_realsize', '_display', '_depth']
    repr_classname = "W_DisplayBitmap"

    def __init__(self, space, size, depth):
        self._real_depth_buffer = lltype.malloc(rffi.CArray(rffi.UINT), size, flavor='raw')
        self._realsize = size
        self._depth = depth
        self._display = space.display()
        self.relinquish_display()

    def getclass(self, space):
        return space.w_Bitmap

    def guess_classname(self):
        return "Bitmap"

    # === Object access

    def at0(self, space, index0):
        self = jit.promote(self)
        val = self.getword(index0)
        return space.wrap_uint(val)

    def atput0(self, space, index0, w_value):
        self = jit.promote(self)
        word = space.unwrap_uint(w_value)
        self.setword(index0, word)

    def unwrap_string(self, space):
        # OH GOD! TODO: Make this sane!
        res = []
        for i in range(self._realsize):
            res += [chr(self.getword(i) & r_uint(0x000000ff)),
                    chr((self.getword(i) & r_uint(0x0000ff00)) >> 8),
                    chr((self.getword(i) & r_uint(0x00ff0000)) >> 16),
                    chr((self.getword(i) & r_uint(0xff000000)) >> 24)]
        return "".join(res)

    def getword(self, n):
        assert self.size() > n >= 0
        return r_uint(self._real_depth_buffer[n])

    def setword(self, n, word):
        self._real_depth_buffer[n] = rffi.r_uint(word)

    def size(self):
        return self._realsize

    # === Graphics

    def display(self):
        return jit.promote(self._display)

    def is_headless(self):
        return self._display.is_headless()

    def pixelbuffer(self):
        return self.display().get_pixelbuffer()

    def set_pixelbuffer_word(self, n, word):
        self.pixelbuffer()[n] = rffi.r_uint(word)

    @jit.elidable
    def pixel_per_word(self):
        return constants.BYTES_PER_WORD / (self.display().depth / 8)

    def take_over_display(self):
        # Make sure FrameWrapper.take_over_display() is called first for the correct Frame object.
        self.pixelbuffer_words = self.display().width * self.display().height / self.pixel_per_word()
        self.update_from_buffer()

    def relinquish_display(self):
        self.pixelbuffer_words = 0

    def flush_to_screen(self):
        self.display().flip()

    def word_from_pixel(self, x, y):
        return (x + y * self.display().width) / self.pixel_per_word()

    def force_rectange_to_screen(self, left, right, top, bottom):
        if self.pixelbuffer_words > 0:
            start = max(self.word_from_pixel(left, top), 0)
            stop = min(self.word_from_pixel(right, bottom), self.size() - 1)
            if stop <= start:
                return
            self.force_words(start, stop)

    def force_words(self, start, stop):
        if self.is_headless(): return
        for i in range(stop - start):
            self.set_pixelbuffer_word(i + start, self.getword(i + start))

    def update_from_buffer(self):
        if self.is_headless(): return
        if self.pixelbuffer_words > 0:
            for i in range(self.size()):
                self.set_pixelbuffer_word(i, self.getword(i))

    # === Misc

    def invariant(self):
        return False

    def clone(self, space):
        w_result = W_WordsObject(space, self.getclass(space), self.size())
        for n in range(self.size()):
            w_result.setword(n, self.getword(n))
        return w_result

    def is_array_object(self):
        return True

    def can_become(self, w_other):
        # TODO - implement _become() for this class. Impossible due to _immutable_fields_?
        return False

    def convert_to_c_layout(self):
        return self._real_depth_buffer

    def __del__(self):
        lltype.free(self._real_depth_buffer, flavor='raw')

    def repr_content(self):
        return "len=%d depth=%d %s" % (self.size(), self._depth, self.str_content())

class W_32BitDisplayBitmap(W_DisplayBitmap):
    repr_classname = "W_32BitDisplayBitmap"

    def force_words(self, start, stop):
        if self.is_headless(): return
        if start >= 0 and stop >= 0 and self.size() > stop and self.pixelbuffer_words > stop and stop > start:
            pixbuf = rffi.ptradd(self.display().get_pixelbuffer(), start)
            realbuf = rffi.ptradd(self._real_depth_buffer, start)
            rffi.c_memcpy(
                rffi.cast(rffi.VOIDP, pixbuf),<commit_msg>avoid off-by-one overflow when memcpy'ing to screen<commit_after>from rsqueakvm import constants, error
from rsqueakvm.model.base import W_AbstractObjectWithIdentityHash
from rsqueakvm.model.variable import W_WordsObject
from rsqueakvm.util import system

from rpython.rlib import jit
from rpython.rtyper.lltypesystem import lltype, rffi
from rpython.rlib.rarithmetic import r_uint


def from_words_object(w_obj, form):
    depth = form.depth()
    space = form.space
    size = w_obj.size()
    w_class = w_obj.getclass(space)
    if not w_class.is_same_object(space.w_Bitmap):
        raise error.WrappingError

    if depth < 8:
        w_display_bitmap = W_MappingDisplayBitmap(space, size, depth)
    elif depth == 8:
        w_display_bitmap = W_8BitDisplayBitmap(space, size, depth)
    elif depth == 16:
        w_display_bitmap = W_16BitDisplayBitmap(space, size, depth)
    else:
        assert depth == 32
        w_display_bitmap = W_32BitDisplayBitmap(space, size, depth)

    for idx in range(size):
        w_display_bitmap.setword(idx, w_obj.getword(idx))

    return w_display_bitmap


class W_DisplayBitmap(W_AbstractObjectWithIdentityHash):
    _attrs_ = ['pixelbuffer_words', '_real_depth_buffer', '_realsize', '_display', '_depth']
    _immutable_fields_ = ['pixelbuffer_words?', '_real_depth_buffer', '_realsize', '_display', '_depth']
    repr_classname = "W_DisplayBitmap"

    def __init__(self, space, size, depth):
        self._real_depth_buffer = lltype.malloc(rffi.CArray(rffi.UINT), size, flavor='raw')
        self._realsize = size
        self._depth = depth
        self._display = space.display()
        self.relinquish_display()

    def getclass(self, space):
        return space.w_Bitmap

    def guess_classname(self):
        return "Bitmap"

    # === Object access

    def at0(self, space, index0):
        self = jit.promote(self)
        val = self.getword(index0)
        return space.wrap_uint(val)

    def atput0(self, space, index0, w_value):
        self = jit.promote(self)
        word = space.unwrap_uint(w_value)
        self.setword(index0, word)

    def unwrap_string(self, space):
        # OH GOD! TODO: Make this sane!
        res = []
        for i in range(self._realsize):
            res += [chr(self.getword(i) & r_uint(0x000000ff)),
                    chr((self.getword(i) & r_uint(0x0000ff00)) >> 8),
                    chr((self.getword(i) & r_uint(0x00ff0000)) >> 16),
                    chr((self.getword(i) & r_uint(0xff000000)) >> 24)]
        return "".join(res)

    def getword(self, n):
        assert self.size() > n >= 0
        return r_uint(self._real_depth_buffer[n])

    def setword(self, n, word):
        self._real_depth_buffer[n] = rffi.r_uint(word)

    def size(self):
        return self._realsize

    # === Graphics

    def display(self):
        return jit.promote(self._display)

    def is_headless(self):
        return self._display.is_headless()

    def pixelbuffer(self):
        return self.display().get_pixelbuffer()

    def set_pixelbuffer_word(self, n, word):
        self.pixelbuffer()[n] = rffi.r_uint(word)

    @jit.elidable
    def pixel_per_word(self):
        return constants.BYTES_PER_WORD / (self.display().depth / 8)

    def take_over_display(self):
        # Make sure FrameWrapper.take_over_display() is called first for the correct Frame object.
        self.pixelbuffer_words = self.display().width * self.display().height / self.pixel_per_word()
        self.update_from_buffer()

    def relinquish_display(self):
        self.pixelbuffer_words = 0

    def flush_to_screen(self):
        self.display().flip()

    def word_from_pixel(self, x, y):
        return (x - 1 + (y - 1) * self.display().width) / self.pixel_per_word()

    def force_rectange_to_screen(self, left, right, top, bottom):
        if self.pixelbuffer_words > 0:
            start = max(self.word_from_pixel(left, top), 0)
            stop = min(self.word_from_pixel(right, bottom), self.size() - 1)
            if stop <= start:
                return
            self.force_words(start, stop)

    def force_words(self, start, stop):
        if self.is_headless(): return
        for i in range(stop - start):
            self.set_pixelbuffer_word(i + start, self.getword(i + start))

    def update_from_buffer(self):
        if self.is_headless(): return
        if self.pixelbuffer_words > 0:
            for i in range(self.size()):
                self.set_pixelbuffer_word(i, self.getword(i))

    # === Misc

    def invariant(self):
        return False

    def clone(self, space):
        w_result = W_WordsObject(space, self.getclass(space), self.size())
        for n in range(self.size()):
            w_result.setword(n, self.getword(n))
        return w_result

    def is_array_object(self):
        return True

    def can_become(self, w_other):
        # TODO - implement _become() for this class. Impossible due to _immutable_fields_?
        return False

    def convert_to_c_layout(self):
        return self._real_depth_buffer

    def __del__(self):
        lltype.free(self._real_depth_buffer, flavor='raw')

    def repr_content(self):
        return "len=%d depth=%d %s" % (self.size(), self._depth, self.str_content())

class W_32BitDisplayBitmap(W_DisplayBitmap):
    repr_classname = "W_32BitDisplayBitmap"

    def force_words(self, start, stop):
        if self.is_headless(): return
        if (start >= 0 and stop > start and self.size() > stop and
            self.pixelbuffer_words > stop):
            pixbuf = rffi.ptradd(self.display().get_pixelbuffer(), start)
            realbuf = rffi.ptradd(self._real_depth_buffer, start)
            rffi.c_memcpy(
                rffi.cast(rffi.VOIDP, pixbuf),
{'fraction_non_alphanumeric': 0.11141209367354073, 'fraction_numerical': 0.011097518350227193, 'mean_word_length': 2.761091028590207, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 32, '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': '6116045', 'n_tokens_mistral': 3838, 'n_tokens_neox': 3656, 'n_words': 802}