File: GstAllocatorFastMalloc.cpp

package info (click to toggle)
webkit2gtk 2.48.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 429,764 kB
  • sloc: cpp: 3,697,587; javascript: 194,444; ansic: 169,997; python: 46,499; asm: 19,295; ruby: 18,528; perl: 16,602; xml: 4,650; yacc: 2,360; sh: 2,098; java: 1,993; lex: 1,327; pascal: 366; makefile: 298
file content (204 lines) | stat: -rw-r--r-- 8,658 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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
161
162
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
/*
 *  Copyright (C) 2018 Igalia S.L
 *  Copyright (C) 2018 Metrological Group B.V.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "config.h"
#include "GstAllocatorFastMalloc.h"

#include <gst/gst.h>
#include <wtf/FastMalloc.h>
#include <wtf/MallocSpan.h>
#include <wtf/StdLibExtras.h>
#include <wtf/glib/WTFGType.h>

typedef struct {
    // Instance of the base GstMemory class (we're effectively subclassing it, not unlike GstMemorySystem).
    GstMemory base;
    // Span to the tail of an GstMemoryFastMalloc allocation block where the user memory contents will be stored.
    // gstMemoryFastMallocNew() will allocate enough space to fit such tail after GstMemoryFastMalloc.
    // gstAllocatorFastMallocMemShare() only allocates enough space for the GstMemoryFastMalloc struct and
    // instead shares the `data` span with the original (parent) GstMemoryFastMalloc.
    std::span<uint8_t> data;
} GstMemoryFastMalloc;
//
// Note: when allocating GstMemoryFastMalloc we also allocate a long enough tail to hold the contents
// the user will store. That tail is what will be returned by our map() function.

typedef struct {
} GstAllocatorFastMallocPrivate;

typedef struct {
    GstAllocator parent;
    GstAllocatorFastMallocPrivate* priv;
} GstAllocatorFastMalloc;

typedef struct {
    GstAllocatorClass parent;
} GstAllocatorFastMallocClass;

WEBKIT_DEFINE_TYPE(GstAllocatorFastMalloc, gst_allocator_fast_malloc, GST_TYPE_ALLOCATOR)

#define WEBKIT_GST_ALLOCATOR_FAST_MALLOC_CAST(p) reinterpret_cast<GstAllocatorFastMalloc*>(p)
#define GST_MEMORY_FAST_MALLOC_CAST(p) reinterpret_cast<GstMemoryFastMalloc*>(p)

static GstMemoryFastMalloc* gstMemoryFastMallocNew(GstAllocator* allocator, gsize size, gsize alignment, gsize offset, gsize padding, GstMemoryFlags flags)
{
    ASSERT(G_TYPE_CHECK_INSTANCE_TYPE(allocator, gst_allocator_fast_malloc_get_type()));

    // Alignment should be a (power-of-two - 1). That is GStreamer's convention.
    alignment |= gst_memory_alignment;
    ASSERT(!((alignment + 1) & alignment));

    // GStreamer's allocator requires heap allocations.
    DisableMallocRestrictionsForCurrentThreadScope disableMallocRestrictions;

    // The block we get from FastMalloc consists of:
    // (1) *Header*: GstMemoryFastMalloc struct, plus any extra space needed to satisfy alignment.
    gsize headerSize = (sizeof(GstMemoryFastMalloc) + alignment) & ~alignment; // Rounds up to the nearest alignment unit.
    // (2) *Tail*: the memory that is accessible to the user via GstMemory.map().
    gsize tailSize = offset + size + padding;

    auto totalSize = headerSize + tailSize;
    auto storage = MallocSpan<uint8_t, FastAlignedMalloc>::tryAlignedMalloc(alignment + 1 /* Power of 2 */, totalSize);
    if (!storage)
        return nullptr;
    auto wholeAllocation = storage.leakSpan();

    auto& header = WTF::reinterpretCastSpanStartTo<GstMemoryFastMalloc>(wholeAllocation);
    header.data = wholeAllocation.subspan(headerSize);

    if (offset && (flags & GST_MEMORY_FLAG_ZERO_PREFIXED))
        memsetSpan(header.data.subspan(0, offset), 0);
    if (padding && (flags & GST_MEMORY_FLAG_ZERO_PADDED))
        memsetSpan(header.data.subspan(offset + size, padding), 0);

    gst_memory_init(&header.base, flags, allocator, nullptr, tailSize, alignment, offset, size);
    return &header;
}

static GstMemory* gstAllocatorFastMallocAlloc(GstAllocator* allocator, gsize size, GstAllocationParams* params)
{
    return GST_MEMORY_CAST(gstMemoryFastMallocNew(allocator, size, params->align, params->prefix, params->padding, params->flags));
}

static void gstAllocatorFastMallocFree(GstAllocator* allocator, GstMemory* memory)
{
#if ASSERT_ENABLED
    ASSERT(G_TYPE_CHECK_INSTANCE_TYPE(allocator, gst_allocator_fast_malloc_get_type()));
#else
    UNUSED_PARAM(allocator);
#endif

    fastAlignedFree(memory);
}

static gpointer gstAllocatorFastMallocMemMap(GstMemory* baseMemory, gsize, GstMapFlags)
{
    auto memory = GST_MEMORY_FAST_MALLOC_CAST(baseMemory);
    return memory->data.data();
}

static void gstAllocatorFastMallocMemUnmap(GstMemory*)
{
}

static GstMemory* gstAllocatorFastMallocMemCopy(GstMemory* baseMemory, gssize offset, gssize size)
{
    auto memory = GST_MEMORY_FAST_MALLOC_CAST(baseMemory);
    if (size == static_cast<gssize>(-1))
        size = memory->base.size > static_cast<gsize>(offset) ? memory->base.size - offset : 0;

    auto* copy = gstMemoryFastMallocNew(memory->base.allocator, size, memory->base.align, 0, 0, static_cast<GstMemoryFlags>(0));
    if (!copy)
        return nullptr;

    auto destinationSpan = copy->data.subspan(0, size);
    memcpySpan(destinationSpan, memory->data.subspan(memory->base.offset + offset, size));
    return GST_MEMORY_CAST(copy);
}

static GstMemory* gstAllocatorFastMallocMemShare(GstMemory* baseMemory, gssize offset, gssize size)
{
    auto memory = GST_MEMORY_FAST_MALLOC_CAST(baseMemory);
    GstMemoryFastMalloc* sharedMemory;
    if (!tryFastMalloc(sizeof(GstMemoryFastMalloc)).getValue(sharedMemory))
        return nullptr;

    sharedMemory->data = memory->data;

    if (size == static_cast<gssize>(-1))
        size = memory->base.size - offset;

    auto* parent = memory->base.parent ? memory->base.parent : GST_MEMORY_CAST(memory);
    gst_memory_init(GST_MEMORY_CAST(sharedMemory),
        static_cast<GstMemoryFlags>(GST_MINI_OBJECT_FLAGS(parent) | GST_MINI_OBJECT_FLAG_LOCK_READONLY),
        memory->base.allocator, parent, memory->base.maxsize, memory->base.align,
        memory->base.offset + offset, size);

    return GST_MEMORY_CAST(sharedMemory);
}

// Will be called by gst_memory_is_span() after checking that both GstMemories have
// the same allocators and they both share the same non-null parent.
//
// Returns whether the visible end of the first memory coincides with the visible
// start of the second memory and fills `offset` with the offset of the first memory
// within the visible span of the parent memory.
//
// This is used to avoid expensive concatenation: If the function returns true, it
// means you can use gst_memory_share() on the parent memory with the filled offset
// and the combined size of both memories to get a GstMemory that spans both previous
// memories without doing any copies. See _sysmem_is_span() in GStreamer for reference.
static gboolean gstAllocatorFastMallocMemIsSpan(GstMemory* _memory1, GstMemory* _memory2, gsize* offset)
{
    auto memory1 = GST_MEMORY_FAST_MALLOC_CAST(_memory1);
    auto memory2 = GST_MEMORY_FAST_MALLOC_CAST(_memory2);
    if (offset) {
        auto parent = GST_MEMORY_FAST_MALLOC_CAST(memory1->base.parent);
        ASSERT(parent);
        *offset = memory1->base.offset - parent->base.offset;
    }

    auto visibleSpan1 = memory1->data.subspan(memory1->base.offset, memory1->base.size);
    auto visibleSpan2 = memory2->data.subspan(memory2->base.offset, memory2->base.size);
    return visibleSpan1.end() == visibleSpan2.begin();
}

static void gstAllocatorFastMallocConstructed(GObject* object)
{
    G_OBJECT_CLASS(gst_allocator_fast_malloc_parent_class)->constructed(object);
    GST_OBJECT_FLAG_SET(GST_OBJECT_CAST(object), GST_OBJECT_FLAG_MAY_BE_LEAKED);

    auto allocator = GST_ALLOCATOR_CAST(object);
    allocator->mem_type = "FastMalloc";
    allocator->mem_map = gstAllocatorFastMallocMemMap;
    allocator->mem_unmap = gstAllocatorFastMallocMemUnmap;
    allocator->mem_copy = gstAllocatorFastMallocMemCopy;
    allocator->mem_share = gstAllocatorFastMallocMemShare;
    allocator->mem_is_span = gstAllocatorFastMallocMemIsSpan;
}

static void gst_allocator_fast_malloc_class_init(GstAllocatorFastMallocClass* klass)
{
    auto gobjectClass = G_OBJECT_CLASS(klass);
    gobjectClass->constructed = gstAllocatorFastMallocConstructed;

    auto gstAllocatorClass = GST_ALLOCATOR_CLASS(klass);
    gstAllocatorClass->alloc = gstAllocatorFastMallocAlloc;
    gstAllocatorClass->free = gstAllocatorFastMallocFree;
}