1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/lib/ui/painting/immutable_buffer.h"
6
7#include "flutter/lib/ui/ui_dart_state.h"
8#include "third_party/tonic/converter/dart_converter.h"
9#include "third_party/tonic/dart_args.h"
10#include "third_party/tonic/dart_binding_macros.h"
11
12#if OS_ANDROID
13#include <sys/mman.h>
14#endif
15
16namespace flutter {
17
18IMPLEMENT_WRAPPERTYPEINFO(ui, ImmutableBuffer);
19
20#define FOR_EACH_BINDING(V) \
21 V(ImmutableBuffer, dispose) \
22 V(ImmutableBuffer, length)
23
24FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
25
26ImmutableBuffer::~ImmutableBuffer() {}
27
28void ImmutableBuffer::RegisterNatives(tonic::DartLibraryNatives* natives) {
29 natives->Register({{"ImmutableBuffer_init", ImmutableBuffer::init, 3, true},
30 FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
31}
32
33void ImmutableBuffer::init(Dart_NativeArguments args) {
34 Dart_Handle callback_handle = Dart_GetNativeArgument(args, 2);
35 if (!Dart_IsClosure(callback_handle)) {
36 Dart_SetReturnValue(args, tonic::ToDart("Callback must be a function"));
37 return;
38 }
39
40 Dart_Handle buffer_handle = Dart_GetNativeArgument(args, 0);
41 tonic::Uint8List data = tonic::Uint8List(Dart_GetNativeArgument(args, 1));
42
43 auto sk_data = MakeSkDataWithCopy(data.data(), data.num_elements());
44 data.Release();
45 auto buffer = fml::MakeRefCounted<ImmutableBuffer>(sk_data);
46 buffer->AssociateWithDartWrapper(buffer_handle);
47 tonic::DartInvoke(callback_handle, {Dart_TypeVoid()});
48}
49
50size_t ImmutableBuffer::GetAllocationSize() const {
51 return sizeof(ImmutableBuffer) + data_->size();
52}
53
54#if OS_ANDROID
55
56// Compressed image buffers are allocated on the UI thread but are deleted on a
57// decoder worker thread. Android's implementation of malloc appears to
58// continue growing the native heap size when the allocating thread is
59// different from the freeing thread. To work around this, create an SkData
60// backed by an anonymous mapping.
61sk_sp<SkData> ImmutableBuffer::MakeSkDataWithCopy(const void* data,
62 size_t length) {
63 if (length == 0) {
64 return SkData::MakeEmpty();
65 }
66
67 size_t mapping_length = length + sizeof(size_t);
68 void* mapping = ::mmap(nullptr, mapping_length, PROT_READ | PROT_WRITE,
69 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
70
71 if (mapping == MAP_FAILED) {
72 return SkData::MakeEmpty();
73 }
74
75 *reinterpret_cast<size_t*>(mapping) = mapping_length;
76 void* mapping_data = reinterpret_cast<char*>(mapping) + sizeof(size_t);
77 ::memcpy(mapping_data, data, length);
78
79 SkData::ReleaseProc proc = [](const void* ptr, void* context) {
80 size_t* size_ptr = reinterpret_cast<size_t*>(context);
81 FML_DCHECK(ptr == size_ptr + 1);
82 if (::munmap(const_cast<void*>(context), *size_ptr) == -1) {
83 FML_LOG(ERROR) << "munmap of codec SkData failed";
84 }
85 };
86
87 return SkData::MakeWithProc(mapping_data, length, proc, mapping);
88}
89
90#else
91
92sk_sp<SkData> ImmutableBuffer::MakeSkDataWithCopy(const void* data,
93 size_t length) {
94 return SkData::MakeWithCopy(data, length);
95}
96
97#endif // OS_ANDROID
98
99} // namespace flutter
100