Skip to content

malloc_buffer

MallocBuffer

Bases: Buffer

A buffer that uses buffers managed by malloc to store data.

Source code in src/finch/codegen/buffers/malloc_buffer.py
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
class MallocBuffer(Buffer):
    """
    A buffer that uses buffers managed by malloc to store data.
    """

    def __init__(self, length: int, dtype: FType, data=None):
        """
        Constructor for the MallocBuffer class.

        length (int): the length of the malloc array.
        dtype (FType): the type that the buffer will be based on.
        data (optional): a list of data to initialize the buffer with.
        """
        self._dtype = dtype
        self._c_dtype = c_type(self._dtype)
        self.buffer = ctypes.pointer(CMallocBufferStruct())

        MallocBufferBackend.library(self.ftype).init(
            self.buffer,
            serialize_to_c(self.length_type, ctypes.sizeof(self._c_dtype)),
            serialize_to_c(self.length_type, length),
        )
        if data is None:
            return
        if len(data) > length:
            raise IndexError

        for idx, elt in enumerate(data):
            self.castbuffer[idx] = serialize_to_c(self._dtype, elt)

    def __del__(self):
        """
        Frees the mallocbuffer stored inside.
        """
        if hasattr(self, "buffer"):
            MallocBufferBackend.library(self.ftype).free(self.buffer)

    @property
    def castbuffer(self):
        return ctypes.cast(self.buffer.contents.data, ctypes.POINTER(self._c_dtype))

    @property
    def ftype(self):
        """
        Returns the ftype of the buffer, which is a MallocBufferFType.
        """
        return MallocBufferFType(self._dtype)

    # TODO should be property
    def length(self):
        return np.intp(self.buffer.contents.length)

    def load(self, index):
        value = self.castbuffer[index]
        return construct_from_c(self.ftype.element_type, value)

    def store(self, index, value):
        value = serialize_to_c(self.ftype.element_type, value)
        self.castbuffer[index] = value

    def resize(self, new_length):
        # we effectively have to run an entire serialization cycle ourselves.
        old_length = serialize_to_c(self.ftype.length_type, self.length())
        new_length = serialize_to_c(self.ftype.length_type, new_length)
        newptr = MallocBufferBackend.library(self.ftype).resize(
            self.castbuffer, old_length, new_length
        )
        self.buffer.contents.data = ctypes.cast(newptr, ctypes.c_void_p)
        self.buffer.contents.length = new_length

    def __str__(self):
        array = self.castbuffer[: self.length()]
        return f"malloc_buf({array})"

ftype property

Returns the ftype of the buffer, which is a MallocBufferFType.

__del__()

Frees the mallocbuffer stored inside.

Source code in src/finch/codegen/buffers/malloc_buffer.py
193
194
195
196
197
198
def __del__(self):
    """
    Frees the mallocbuffer stored inside.
    """
    if hasattr(self, "buffer"):
        MallocBufferBackend.library(self.ftype).free(self.buffer)

__init__(length, dtype, data=None)

Constructor for the MallocBuffer class.

length (int): the length of the malloc array. dtype (FType): the type that the buffer will be based on. data (optional): a list of data to initialize the buffer with.

Source code in src/finch/codegen/buffers/malloc_buffer.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def __init__(self, length: int, dtype: FType, data=None):
    """
    Constructor for the MallocBuffer class.

    length (int): the length of the malloc array.
    dtype (FType): the type that the buffer will be based on.
    data (optional): a list of data to initialize the buffer with.
    """
    self._dtype = dtype
    self._c_dtype = c_type(self._dtype)
    self.buffer = ctypes.pointer(CMallocBufferStruct())

    MallocBufferBackend.library(self.ftype).init(
        self.buffer,
        serialize_to_c(self.length_type, ctypes.sizeof(self._c_dtype)),
        serialize_to_c(self.length_type, length),
    )
    if data is None:
        return
    if len(data) > length:
        raise IndexError

    for idx, elt in enumerate(data):
        self.castbuffer[idx] = serialize_to_c(self._dtype, elt)

MallocBufferBackend

Source code in src/finch/codegen/buffers/malloc_buffer.py
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
class MallocBufferBackend:
    _library: dict[Hashable, CMallocBufferLibrary] = {}

    @classmethod
    def gen_code(
        cls,
        ctx: CContext,
        ftype: MallocBufferFType,
        inline: bool = False,
    ) -> CMallocBufferMethods:
        ctx.add_header("#include <string.h>")
        ctx.add_header("#include <stdlib.h>")
        ctx.add_header("#include <stdio.h>")

        methods = CMallocBufferMethods(
            init=ctx.freshen("mallocbuffer_init"),
            free=ctx.freshen("mallocbuffer_free"),
            resize=ctx.freshen("mallocbuffer_resize"),
        )
        ctx.buffer_methods[ftype] = methods

        buffer_type = ctx.ctype_name(CMallocBufferStruct)
        elt_type = ctx.ctype_name(c_type(ftype.element_type))
        length_type = ctx.ctype_name(c_type(ftype.length_type))

        inline_s = "static inline " if inline else "FINCH_EXPORT "
        libcode = dedent(
            f"""
            {inline_s}{elt_type}*
            {methods.resize}(
                {elt_type}* data,
                {length_type} len_old,
                {length_type} len_new
            ) {{
                data = realloc(data, sizeof({elt_type}) * len_new);
                if (data == 0) {{
                    fprintf(stderr, "Malloc Failed!\\n");
                    exit(1);
                }}
                if (len_new > len_old) {{
                    memset(&data[len_old], 0, (len_new - len_old) * sizeof({elt_type}));
                }}
                return data;
            }}
            // methods below are not used by the kernel.
            {inline_s}void
            {methods.free}({buffer_type} *m) {{
                free(m->data);
                m->data = 0;
                m->length = 0;
            }}
            {inline_s}void
            {methods.init}(
                {buffer_type} *m,
                {length_type} datasize,
                {length_type} length
            ) {{
                m->length = length;
                m->data = malloc(length * datasize);
                if (m->data != 0)
                    memset(m->data, 0, length * datasize);
            }}
            """
        )
        ctx.add_header(libcode)
        return methods

    @classmethod
    def library(cls, ftype: MallocBufferFType) -> CMallocBufferLibrary:
        """
        Returns compiled library to operate on a buffer outside of
        the kernel.
        """
        if ftype in cls._library:
            return cls._library[ftype]
        ctx = CContext()
        methods = cls.gen_code(ctx, ftype)
        lib = load_shared_lib(ctx.emit_global())

        length_type = c_type(ftype.length_type)

        resize_func = getattr(lib, methods.resize)
        resize_func.argtypes = [
            ctypes.POINTER(c_type(ftype.element_type)),
            length_type,
            length_type,
        ]
        resize_func.restype = ctypes.POINTER(c_type(ftype.element_type))

        init_func = getattr(lib, methods.init)
        init_func.argtypes = [
            ctypes.POINTER(CMallocBufferStruct),
            length_type,
            length_type,
        ]
        init_func.restype = None

        free_func = getattr(lib, methods.free)
        free_func.argtypes = [
            ctypes.POINTER(CMallocBufferStruct),
        ]
        free_func.restype = None

        cls._library[ftype] = CMallocBufferLibrary(lib, methods)
        return cls._library[ftype]

library(ftype) classmethod

Returns compiled library to operate on a buffer outside of the kernel.

Source code in src/finch/codegen/buffers/malloc_buffer.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
@classmethod
def library(cls, ftype: MallocBufferFType) -> CMallocBufferLibrary:
    """
    Returns compiled library to operate on a buffer outside of
    the kernel.
    """
    if ftype in cls._library:
        return cls._library[ftype]
    ctx = CContext()
    methods = cls.gen_code(ctx, ftype)
    lib = load_shared_lib(ctx.emit_global())

    length_type = c_type(ftype.length_type)

    resize_func = getattr(lib, methods.resize)
    resize_func.argtypes = [
        ctypes.POINTER(c_type(ftype.element_type)),
        length_type,
        length_type,
    ]
    resize_func.restype = ctypes.POINTER(c_type(ftype.element_type))

    init_func = getattr(lib, methods.init)
    init_func.argtypes = [
        ctypes.POINTER(CMallocBufferStruct),
        length_type,
        length_type,
    ]
    init_func.restype = None

    free_func = getattr(lib, methods.free)
    free_func.argtypes = [
        ctypes.POINTER(CMallocBufferStruct),
    ]
    free_func.restype = None

    cls._library[ftype] = CMallocBufferLibrary(lib, methods)
    return cls._library[ftype]

MallocBufferFType

Bases: CBufferFType, CUnpackableFType

A ftype for buffers that uses libc-provided malloc functions. This is a concrete implementation of the BufferFType class.

This does not support the numba backend.

Source code in src/finch/codegen/buffers/malloc_buffer.py
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
class MallocBufferFType(CBufferFType, CUnpackableFType):
    """
    A ftype for buffers that uses libc-provided malloc functions. This is a
    concrete implementation of the BufferFType class.

    This does not support the numba backend.
    """

    def __init__(self, element_type: FType):
        self._element_type = element_type

    def __eq__(self, other):
        if not isinstance(other, MallocBufferFType):
            return False
        return self._element_type == other._element_type

    def __str__(self):
        return f"malloc_buf_t({qual_str(self._element_type)})"

    def __repr__(self):
        return f"MallocBufferFType({qual_str(self._element_type)})"

    @property
    def length_type(self):
        """
        Returns the type used for the length of the buffer.
        """
        return ftypes.intp

    @property
    def element_type(self):
        """
        Returns the type of elements stored in the buffer. This will be a ctypes array.
        """
        return self._element_type

    def __hash__(self):
        return hash(("MallocBufferFType", self._element_type))

    def __call__(self, len: int = 0):
        return MallocBuffer(len, self._element_type)

    def c_type(self):
        return ctypes.POINTER(CMallocBufferStruct)

    def c_length(self, ctx: CContext, buf: CBufferFields):
        return buf.length

    def c_data(self, ctx: CContext, buf: CBufferFields):
        return buf.data

    def c_load(self, ctx: CContext, buf: CBufferFields, idx: AssemblyExpression):
        return f"({buf.data})[{ctx(idx)}]"

    def c_store(
        self,
        ctx: CContext,
        buf: CBufferFields,
        idx: AssemblyExpression,
        value: AssemblyExpression,
    ):
        ctx.exec(f"{ctx.feed}({buf.data})[{ctx(idx)}] = {ctx(value)};")

    def c_resize(self, ctx: CContext, buf: CBufferFields, new_len: AssemblyExpression):
        if self not in ctx.buffer_methods:
            raise Exception("A Mallocbuffer must be unpacked before being operated on!")

        methods: CMallocBufferMethods = ctx.buffer_methods[self]

        new_len = ctx(ctx.cache("len", new_len))
        data = buf.data
        length = buf.length

        ctx.exec(
            f"{ctx.feed}{data} = {methods.resize}({data}, {length}, {new_len});\n"
            f"{ctx.feed}{length} = {new_len};"
        )
        return

    def c_unpack(self, ctx: CContext, var_n, val):
        """
        Unpack the malloc buffer into C context.
        """
        data = ctx.freshen(var_n, "data")
        length = ctx.freshen(var_n, "length")
        t = ctx.ctype_name(c_type(self.element_type))
        ctx.add_header("#include <stddef.h>")

        if self not in ctx.buffer_methods:
            MallocBufferBackend.gen_code(ctx, self, inline=True)

        ctx.exec(
            f"{ctx.feed}{t}* {data} = ({t}*){ctx(val)}->data;\n"
            f"{ctx.feed}size_t {length} = {ctx(val)}->length;"
        )

        return CBufferFields(data, length, var_n)

    def c_repack(self, ctx, var_n: str, obj: CBufferFields):
        """
        Repack the buffer from C context.
        """
        ctx.exec(
            f"{ctx.feed}{var_n}->data = (void*){obj.data};\n"
            f"{ctx.feed}{var_n}->length = {obj.length};"
        )
        return

    def serialize_to_c(self, obj: MallocBuffer):
        """
        Serialize the Malloc buffer to a C-compatible structure.
        This is trivial.
        """
        return obj.buffer

    def deserialize_from_c(self, obj, c_buffer):
        pass
        # this is handled by the resize callback

    def construct_from_c(self, c_buffer):
        """
        Construct a MallocBuffer from a C-compatible structure.
        """
        return c_buffer.contents

element_type property

Returns the type of elements stored in the buffer. This will be a ctypes array.

length_type property

Returns the type used for the length of the buffer.

c_repack(ctx, var_n, obj)

Repack the buffer from C context.

Source code in src/finch/codegen/buffers/malloc_buffer.py
336
337
338
339
340
341
342
343
344
def c_repack(self, ctx, var_n: str, obj: CBufferFields):
    """
    Repack the buffer from C context.
    """
    ctx.exec(
        f"{ctx.feed}{var_n}->data = (void*){obj.data};\n"
        f"{ctx.feed}{var_n}->length = {obj.length};"
    )
    return

c_unpack(ctx, var_n, val)

Unpack the malloc buffer into C context.

Source code in src/finch/codegen/buffers/malloc_buffer.py
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
def c_unpack(self, ctx: CContext, var_n, val):
    """
    Unpack the malloc buffer into C context.
    """
    data = ctx.freshen(var_n, "data")
    length = ctx.freshen(var_n, "length")
    t = ctx.ctype_name(c_type(self.element_type))
    ctx.add_header("#include <stddef.h>")

    if self not in ctx.buffer_methods:
        MallocBufferBackend.gen_code(ctx, self, inline=True)

    ctx.exec(
        f"{ctx.feed}{t}* {data} = ({t}*){ctx(val)}->data;\n"
        f"{ctx.feed}size_t {length} = {ctx(val)}->length;"
    )

    return CBufferFields(data, length, var_n)

construct_from_c(c_buffer)

Construct a MallocBuffer from a C-compatible structure.

Source code in src/finch/codegen/buffers/malloc_buffer.py
357
358
359
360
361
def construct_from_c(self, c_buffer):
    """
    Construct a MallocBuffer from a C-compatible structure.
    """
    return c_buffer.contents

serialize_to_c(obj)

Serialize the Malloc buffer to a C-compatible structure. This is trivial.

Source code in src/finch/codegen/buffers/malloc_buffer.py
346
347
348
349
350
351
def serialize_to_c(self, obj: MallocBuffer):
    """
    Serialize the Malloc buffer to a C-compatible structure.
    This is trivial.
    """
    return obj.buffer