this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "memoryview-builtins.h"
3
4#include "builtins.h"
5#include "bytes-builtins.h"
6#include "float-builtins.h"
7#include "int-builtins.h"
8#include "mmap-module.h"
9#include "type-builtins.h"
10
11namespace py {
12
13static const BuiltinAttribute kMemoryViewAttributes[] = {
14 {ID(_memoryview__buffer), RawMemoryView::kBufferOffset,
15 AttributeFlags::kHidden},
16 {ID(format), RawMemoryView::kFormatOffset, AttributeFlags::kReadOnly},
17 {ID(_memoryview__length), RawMemoryView::kLengthOffset,
18 AttributeFlags::kHidden},
19 {ID(readonly), RawMemoryView::kReadOnlyOffset, AttributeFlags::kReadOnly},
20 {ID(obj), RawMemoryView::kObjectOffset, AttributeFlags::kReadOnly},
21 {ID(shape), RawMemoryView::kShapeOffset, AttributeFlags::kReadOnly},
22 {ID(_memoryview__start), RawMemoryView::kStartOffset,
23 AttributeFlags::kHidden},
24 {ID(strides), RawMemoryView::kStridesOffset, AttributeFlags::kReadOnly},
25 {ID(ndim), RawMemoryView::kNdimOffset, AttributeFlags::kReadOnly},
26};
27
28void initializeMemoryViewType(Thread* thread) {
29 addBuiltinType(thread, ID(memoryview), LayoutId::kMemoryView,
30 /*superclass_id=*/LayoutId::kObject, kMemoryViewAttributes,
31 MemoryView::kSize, /*basetype=*/false);
32}
33
34static char formatChar(const Str& format) {
35 if (format.length() == 2) {
36 if (format.byteAt(0) != '@') return -1;
37 return format.byteAt(1);
38 }
39 if (format.length() != 1) return -1;
40 return format.byteAt(0);
41}
42
43static word itemSize(char format) {
44 switch (format) {
45 case 'c':
46 case 'b':
47 case 'B':
48 return kByteSize;
49 case 'h':
50 case 'H':
51 return kShortSize;
52 case 'i':
53 case 'I':
54 return kIntSize;
55 case 'l':
56 case 'L':
57 return kLongSize;
58 case 'q':
59 case 'Q':
60 return kLongLongSize;
61 case 'n':
62 case 'N':
63 return kWordSize;
64 case 'f':
65 return kFloatSize;
66 case 'd':
67 return kDoubleSize;
68 case '?':
69 return kBoolSize;
70 case 'P':
71 return kPointerSize;
72 default:
73 return -1;
74 }
75}
76
77word memoryviewItemsize(Thread* thread, const MemoryView& view) {
78 HandleScope scope(thread);
79 Str format(&scope, view.format());
80 char format_c = formatChar(format);
81 DCHECK(format_c > 0, "invalid memoryview");
82 word item_size = itemSize(format_c);
83 DCHECK(item_size > 0, "invalid memoryview");
84 return item_size;
85}
86
87static RawObject raiseInvalidValueError(Thread* thread, char format) {
88 return thread->raiseWithFmt(LayoutId::kValueError,
89 "memoryview: invalid value for format '%c'",
90 format);
91}
92
93static RawObject raiseInvalidTypeError(Thread* thread, char format) {
94 return thread->raiseWithFmt(
95 LayoutId::kTypeError, "memoryview: invalid type for format '%c'", format);
96}
97
98static bool isIntFormat(char format) {
99 switch (format) {
100 case 'b':
101 FALLTHROUGH;
102 case 'h':
103 FALLTHROUGH;
104 case 'i':
105 FALLTHROUGH;
106 case 'l':
107 FALLTHROUGH;
108 case 'B':
109 FALLTHROUGH;
110 case 'H':
111 FALLTHROUGH;
112 case 'I':
113 FALLTHROUGH;
114 case 'L':
115 FALLTHROUGH;
116 case 'q':
117 FALLTHROUGH;
118 case 'Q':
119 FALLTHROUGH;
120 case 'n':
121 FALLTHROUGH;
122 case 'N':
123 FALLTHROUGH;
124 case 'P':
125 return true;
126 default:
127 return false;
128 }
129}
130
131static RawObject packObject(Thread* thread, uword address, char format,
132 word index, RawObject value) {
133 byte* dst = reinterpret_cast<byte*>(address + index);
134 if (isIntFormat(format)) {
135 if (!value.isInt()) return Unbound::object();
136 switch (format) {
137 case 'b': {
138 OptInt<char> opt_val = RawInt::cast(value).asInt<char>();
139 if (opt_val.error != CastError::None) {
140 return raiseInvalidValueError(thread, format);
141 }
142 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
143 break;
144 }
145 case 'h': {
146 OptInt<short> opt_val = RawInt::cast(value).asInt<short>();
147 if (opt_val.error != CastError::None) {
148 return raiseInvalidValueError(thread, format);
149 }
150 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
151 break;
152 }
153 case 'i': {
154 OptInt<int> opt_val = RawInt::cast(value).asInt<int>();
155 if (opt_val.error != CastError::None) {
156 return raiseInvalidValueError(thread, format);
157 }
158 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
159 break;
160 }
161 case 'l': {
162 OptInt<long> opt_val = RawInt::cast(value).asInt<long>();
163 if (opt_val.error != CastError::None) {
164 return raiseInvalidValueError(thread, format);
165 }
166 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
167 break;
168 }
169 case 'B': {
170 OptInt<unsigned char> opt_val =
171 RawInt::cast(value).asInt<unsigned char>();
172 if (opt_val.error != CastError::None) {
173 return raiseInvalidValueError(thread, format);
174 }
175 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
176 break;
177 }
178 case 'H': {
179 OptInt<unsigned short> opt_val =
180 RawInt::cast(value).asInt<unsigned short>();
181 if (opt_val.error != CastError::None) {
182 return raiseInvalidValueError(thread, format);
183 }
184 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
185 break;
186 }
187 case 'I': {
188 OptInt<unsigned int> opt_val =
189 RawInt::cast(value).asInt<unsigned int>();
190 if (opt_val.error != CastError::None) {
191 return raiseInvalidValueError(thread, format);
192 }
193 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
194 break;
195 }
196 case 'L': {
197 OptInt<unsigned long> opt_val =
198 RawInt::cast(value).asInt<unsigned long>();
199 if (opt_val.error != CastError::None) {
200 return raiseInvalidValueError(thread, format);
201 }
202 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
203 break;
204 }
205 case 'q': {
206 OptInt<long long> opt_val = RawInt::cast(value).asInt<long long>();
207 if (opt_val.error != CastError::None) {
208 return raiseInvalidValueError(thread, format);
209 }
210 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
211 break;
212 }
213 case 'Q': {
214 OptInt<unsigned long long> opt_val =
215 RawInt::cast(value).asInt<unsigned long long>();
216 if (opt_val.error != CastError::None) {
217 return raiseInvalidValueError(thread, format);
218 }
219 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
220 break;
221 }
222 case 'n': {
223 OptInt<ssize_t> opt_val = RawInt::cast(value).asInt<ssize_t>();
224 if (opt_val.error != CastError::None) {
225 return raiseInvalidValueError(thread, format);
226 }
227 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
228 break;
229 }
230 case 'N': {
231 OptInt<size_t> opt_val = RawInt::cast(value).asInt<size_t>();
232 if (opt_val.error != CastError::None) {
233 return raiseInvalidValueError(thread, format);
234 }
235 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
236 break;
237 }
238 case 'P': {
239 OptInt<uintptr_t> opt_val = RawInt::cast(value).asInt<uintptr_t>();
240 if (opt_val.error != CastError::None) {
241 return raiseInvalidValueError(thread, format);
242 }
243 std::memcpy(dst, &opt_val.value, sizeof(opt_val.value));
244 break;
245 }
246 }
247 return NoneType::object();
248 }
249
250 switch (format) {
251 case 'f': {
252 if (!value.isFloat()) return Unbound::object();
253 float value_float = Float::cast(floatUnderlying(value)).value();
254 std::memcpy(dst, &value_float, sizeof(value_float));
255 return NoneType::object();
256 }
257
258 case 'd': {
259 if (!value.isFloat()) return Unbound::object();
260 double value_double = Float::cast(floatUnderlying(value)).value();
261 std::memcpy(dst, &value_double, sizeof(value_double));
262 return NoneType::object();
263 }
264
265 case 'c': {
266 if (!value.isBytes()) return raiseInvalidTypeError(thread, format);
267 RawBytes value_bytes = bytesUnderlying(value);
268 if (value_bytes.length() != 1) {
269 return raiseInvalidValueError(thread, format);
270 }
271 *dst = value_bytes.byteAt(0);
272 return NoneType::object();
273 }
274
275 case '?': {
276 if (!value.isBool()) return Unbound::object();
277 bool value_bool = Bool::cast(value).value();
278 std::memcpy(dst, &value_bool, sizeof(value_bool));
279 return NoneType::object();
280 }
281 default:
282 UNREACHABLE("invalid format");
283 }
284 return NoneType::object();
285}
286
287static RawObject unpackObject(Thread* thread, uword address, word length,
288 char format, word index) {
289 Runtime* runtime = thread->runtime();
290 DCHECK_INDEX(index, length - static_cast<word>(itemSize(format) - 1));
291 byte* src = reinterpret_cast<byte*>(address + index);
292 switch (format) {
293 case 'c':
294 return runtime->newBytes(1, Utils::readBytes<byte>(src));
295 case 'b':
296 return RawSmallInt::fromWord(Utils::readBytes<signed char>(src));
297 case 'B':
298 return RawSmallInt::fromWord(Utils::readBytes<unsigned char>(src));
299 case 'h':
300 return RawSmallInt::fromWord(Utils::readBytes<short>(src));
301 case 'H':
302 return RawSmallInt::fromWord(Utils::readBytes<unsigned short>(src));
303 case 'i':
304 return runtime->newInt(Utils::readBytes<int>(src));
305 case 'I':
306 return runtime->newInt(Utils::readBytes<unsigned int>(src));
307 case 'l':
308 return runtime->newInt(Utils::readBytes<long>(src));
309 case 'L':
310 return runtime->newIntFromUnsigned(Utils::readBytes<unsigned long>(src));
311 case 'q':
312 return runtime->newInt(Utils::readBytes<long long>(src));
313 case 'Q':
314 return runtime->newIntFromUnsigned(
315 Utils::readBytes<unsigned long long>(src));
316 case 'n':
317 return runtime->newInt(Utils::readBytes<ssize_t>(src));
318 case 'N':
319 return runtime->newIntFromUnsigned(Utils::readBytes<size_t>(src));
320 case 'P':
321 return runtime->newIntFromCPtr(Utils::readBytes<void*>(src));
322 case 'f':
323 return runtime->newFloat(Utils::readBytes<float>(src));
324 case 'd':
325 return runtime->newFloat(Utils::readBytes<double>(src));
326 case '?': {
327 return Bool::fromBool(Utils::readBytes<byte>(src) != 0);
328 }
329 default:
330 UNREACHABLE("invalid format");
331 }
332}
333
334// Helper function that returns the location within the memoryview buffer to
335// find requested index
336static word bufferIndex(const MemoryView& view, word index) {
337 word step = intUnderlying(Tuple::cast(view.strides()).at(0)).asWord();
338 if (step != 1) {
339 UNIMPLEMENTED("Stride != 1 is not yet supported");
340 }
341 DCHECK_INDEX(index, view.length());
342 return view.start() + index;
343}
344
345RawObject memoryviewGetitem(Thread* thread, const MemoryView& view,
346 word index) {
347 HandleScope scope(thread);
348 Object buffer(&scope, view.buffer());
349 Runtime* runtime = thread->runtime();
350
351 // TODO(T36619828) support str subclasses
352 Str format(&scope, view.format());
353 char format_c = formatChar(format);
354 // TODO(T58046846): Replace DCHECK(char > 0) checks
355 DCHECK(format_c > 0, "invalid memoryview");
356 word item_size = itemSize(format_c);
357 DCHECK(item_size > 0, "invalid memoryview");
358 word buffer_index = bufferIndex(view, index);
359
360 if (runtime->isInstanceOfBytes(*buffer)) {
361 // TODO(T38246066) support bytes subclasses
362 if (buffer.isLargeBytes()) {
363 LargeBytes bytes(&scope, *buffer);
364 return unpackObject(thread, bytes.address(), bytes.length(), format_c,
365 buffer_index);
366 }
367 CHECK(buffer.isSmallBytes(),
368 "memoryview.__getitem__ with non bytes/memory");
369 Bytes bytes(&scope, *buffer);
370 byte bytes_buffer[SmallBytes::kMaxLength];
371 bytes.copyTo(bytes_buffer, bytes.length());
372 return unpackObject(thread, reinterpret_cast<uword>(bytes_buffer),
373 bytes.length(), format_c, buffer_index);
374 }
375 CHECK(buffer.isPointer(), "memoryview.__getitem__ with non bytes/memory");
376 void* cptr = Pointer::cast(*buffer).cptr();
377 word ptr_length = Pointer::cast(*buffer).length();
378 return unpackObject(thread, reinterpret_cast<uword>(cptr), ptr_length,
379 format_c, buffer_index);
380}
381
382RawObject memoryviewGetslice(Thread* thread, const MemoryView& view, word start,
383 word stop, word step) {
384 if (step != 1) {
385 UNIMPLEMENTED("Stride != 1 is not yet supported");
386 }
387
388 HandleScope scope(thread);
389 Runtime* runtime = thread->runtime();
390
391 Str format(&scope, view.format());
392 char format_c = formatChar(format);
393 // TODO(T58046846): Replace DCHECK(char > 0) checks
394 DCHECK(format_c > 0, "invalid memoryview");
395 word item_size = itemSize(format_c);
396 DCHECK(item_size > 0, "invalid memoryview");
397 word slice_len = Slice::length(start, stop, step);
398 word slice_byte_size = slice_len * item_size;
399
400 Object buffer(&scope, view.buffer());
401 Object obj(&scope, view.object());
402 MemoryView result(
403 &scope, runtime->newMemoryView(
404 thread, obj, buffer, slice_byte_size,
405 view.readOnly() ? ReadOnly::ReadOnly : ReadOnly::ReadWrite));
406 result.setFormat(view.format());
407 result.setStart(view.start() + start * item_size);
408 return *result;
409}
410
411RawObject memoryviewSetitem(Thread* thread, const MemoryView& view, word index,
412 const Object& value) {
413 HandleScope scope(thread);
414 Object buffer(&scope, view.buffer());
415 Str format(&scope, view.format());
416 char fmt = formatChar(format);
417 // TODO(T58046846): Replace DCHECK(char > 0) checks
418 DCHECK(fmt > 0, "invalid memoryview");
419 word item_size = itemSize(fmt);
420 DCHECK(item_size > 0, "invalid memoryview");
421
422 word buffer_index = bufferIndex(view, index);
423 if (buffer.isMutableBytes()) {
424 return packObject(thread, LargeBytes::cast(*buffer).address(), fmt,
425 buffer_index, *value);
426 }
427 DCHECK(buffer.isPointer(), "memoryview.__setitem__ with non bytes/memory");
428 void* cptr = Pointer::cast(*buffer).cptr();
429 return packObject(thread, reinterpret_cast<uword>(cptr), fmt, buffer_index,
430 *value);
431}
432
433static RawObject raiseDifferentStructureError(Thread* thread) {
434 return thread->raiseWithFmt(
435 LayoutId::kValueError,
436 "memoryview assignment: lvalue and rvalue have different structures");
437}
438
439RawObject memoryviewSetslice(Thread* thread, const MemoryView& view, word start,
440 word stop, word step, word slice_len,
441 const Object& value_obj) {
442 HandleScope scope(thread);
443 Runtime* runtime = thread->runtime();
444 word stride = intUnderlying(Tuple::cast(view.strides()).at(0)).asWord();
445 if (view.start() != 0 || stride != 1) {
446 UNIMPLEMENTED("Set item with slicing called on a sliced memoryview");
447 }
448
449 Str format(&scope, view.format());
450 char fmt = formatChar(format);
451 // TODO(T58046846): Replace DCHECK(char > 0) checks
452 DCHECK(fmt > 0, "invalid memoryview");
453 Object buffer(&scope, view.buffer());
454 Bytes value_bytes(&scope, Bytes::empty());
455 if (runtime->isInstanceOfBytes(*value_obj)) {
456 value_bytes = *value_obj;
457 if (fmt != 'B' || value_bytes.length() != slice_len) {
458 return raiseDifferentStructureError(thread);
459 }
460 } else if (runtime->isInstanceOfBytearray(*value_obj)) {
461 Bytearray value_bytearray(&scope, *value_obj);
462 if (fmt != 'B' || value_bytearray.numItems() != slice_len) {
463 return raiseDifferentStructureError(thread);
464 }
465 value_bytes = value_bytearray.items();
466 } else if (value_obj.isMemoryView()) {
467 MemoryView value(&scope, *value_obj);
468 Str value_format(&scope, value.format());
469 char value_fmt = formatChar(value_format);
470 word item_size = itemSize(value_fmt);
471 DCHECK(item_size > 0, "invalid memoryview");
472 if (fmt != value_fmt || (value.length() / item_size) != slice_len) {
473 return raiseDifferentStructureError(thread);
474 }
475 byte small_bytes_buf[SmallBytes::kMaxLength];
476 uword value_address;
477 Object value_buffer(&scope, value.buffer());
478 if (value_buffer.isLargeBytes()) {
479 value_address = LargeBytes::cast(*value_buffer).address();
480 } else if (value_buffer.isInt()) {
481 value_address = Int::cast(*value_buffer).asInt<uword>().value;
482 } else {
483 DCHECK(value_buffer.isSmallBytes(),
484 "memoryview.__setitem__ with non bytes/memory");
485 Bytes bytes(&scope, *value_buffer);
486 bytes.copyTo(small_bytes_buf, value.length());
487 value_address = reinterpret_cast<uword>(small_bytes_buf);
488 }
489 uword address;
490 if (buffer.isMutableBytes()) {
491 address = LargeBytes::cast(*buffer).address();
492 } else {
493 DCHECK(buffer.isPointer(),
494 "memoryview.__setitem__ with non bytes/memory");
495 address = reinterpret_cast<uword>(Pointer::cast(*buffer).cptr());
496 }
497 if (step == 1 && item_size == 1) {
498 std::memcpy(reinterpret_cast<void*>(address + start),
499 reinterpret_cast<void*>(value_address), slice_len);
500 }
501 start *= item_size;
502 step *= item_size;
503 for (; start < stop; value_address += item_size, start += step) {
504 std::memcpy(reinterpret_cast<void*>(address + start),
505 reinterpret_cast<void*>(value_address), item_size);
506 }
507 return NoneType::object();
508 } else if (runtime->isByteslike(*value_obj)) {
509 UNIMPLEMENTED("unsupported bytes-like type");
510 } else {
511 return thread->raiseWithFmt(LayoutId::kTypeError,
512 "a bytes-like object is required, not '%T'",
513 &value_obj);
514 }
515 byte* address;
516 if (buffer.isMutableBytes()) {
517 address = reinterpret_cast<byte*>(LargeBytes::cast(*buffer).address());
518 } else {
519 DCHECK(buffer.isPointer(), "memoryview.__setitem__ with non bytes/memory");
520 address = static_cast<byte*>(Pointer::cast(*buffer).cptr());
521 }
522 if (step == 1) {
523 value_bytes.copyTo(address + start, slice_len);
524 return NoneType::object();
525 }
526 for (word i = 0; start < stop; i++, start += step) {
527 address[start] = value_bytes.byteAt(i);
528 }
529 return NoneType::object();
530}
531
532static word pow2_remainder(word dividend, word divisor) {
533 DCHECK(divisor > 0 && Utils::isPowerOfTwo(divisor), "must be power of two");
534 word mask = divisor - 1;
535 return dividend & mask;
536}
537
538RawObject METH(memoryview, cast)(Thread* thread, Arguments args) {
539 HandleScope scope(thread);
540 Object self_obj(&scope, args.get(0));
541 if (!self_obj.isMemoryView()) {
542 return thread->raiseRequiresType(self_obj, ID(memoryview));
543 }
544 MemoryView self(&scope, *self_obj);
545
546 Runtime* runtime = thread->runtime();
547 Object format_obj(&scope, args.get(1));
548 if (!runtime->isInstanceOfStr(*format_obj)) {
549 return thread->raiseWithFmt(LayoutId::kTypeError,
550 "format argument must be a string");
551 }
552 Str format(&scope, *format_obj);
553 char format_c = formatChar(format);
554 word item_size;
555 if (format_c < 0 || (item_size = itemSize(format_c)) < 0) {
556 return thread->raiseWithFmt(
557 LayoutId::kValueError,
558 "memoryview: destination must be a native single character format "
559 "prefixed with an optional '@'");
560 }
561
562 word length = self.length();
563 if (pow2_remainder(length, item_size) != 0) {
564 return thread->raiseWithFmt(
565 LayoutId::kValueError,
566 "memoryview: length is not a multiple of itemsize");
567 }
568 Object buffer(&scope, self.buffer());
569 Object obj(&scope, self.object());
570 MemoryView result(
571 &scope, runtime->newMemoryView(
572 thread, obj, buffer, length,
573 self.readOnly() ? ReadOnly::ReadOnly : ReadOnly::ReadWrite));
574 result.setFormat(*format);
575 return *result;
576}
577
578RawObject METH(memoryview, __len__)(Thread* thread, Arguments args) {
579 HandleScope scope(thread);
580 Object self_obj(&scope, args.get(0));
581 if (!self_obj.isMemoryView()) {
582 return thread->raiseRequiresType(self_obj, ID(memoryview));
583 }
584 MemoryView self(&scope, *self_obj);
585 // TODO(T36619828) support str subclasses
586 Str format(&scope, self.format());
587 char format_c = formatChar(format);
588 DCHECK(format_c > 0, "invalid format");
589 word item_size = itemSize(format_c);
590 DCHECK(item_size > 0, "invalid memoryview");
591 return SmallInt::fromWord(self.length() / item_size);
592}
593
594RawObject METH(memoryview, __new__)(Thread* thread, Arguments args) {
595 HandleScope scope(thread);
596 Runtime* runtime = thread->runtime();
597 if (args.get(0) != runtime->typeAt(LayoutId::kMemoryView)) {
598 return thread->raiseWithFmt(LayoutId::kTypeError,
599 "memoryview.__new__(X): X is not 'memoryview'");
600 }
601
602 Object object(&scope, args.get(1));
603 if (runtime->isInstanceOfBytes(*object)) {
604 Bytes bytes(&scope, bytesUnderlying(*object));
605 MemoryView result(
606 &scope, runtime->newMemoryView(thread, object, bytes, bytes.length(),
607 ReadOnly::ReadOnly));
608 return *result;
609 }
610 if (runtime->isInstanceOfBytearray(*object)) {
611 Bytearray bytearray(&scope, *object);
612 Bytes bytes(&scope, bytearray.items());
613 MemoryView result(&scope, runtime->newMemoryView(thread, object, bytes,
614 bytearray.numItems(),
615 ReadOnly::ReadWrite));
616 return *result;
617 }
618 if (object.isMemoryView()) {
619 MemoryView view(&scope, *object);
620 Object buffer(&scope, view.buffer());
621 Object view_obj(&scope, view.object());
622 MemoryView result(
623 &scope, runtime->newMemoryView(thread, view_obj, buffer, view.length(),
624 view.readOnly() ? ReadOnly::ReadOnly
625 : ReadOnly::ReadWrite));
626 result.setFormat(view.format());
627 return *result;
628 }
629 if (object.isMmap()) {
630 Mmap mmap_obj(&scope, *object);
631 Pointer pointer(&scope, mmap_obj.data());
632 MemoryView result(
633 &scope,
634 runtime->newMemoryViewFromCPtr(
635 thread, object, pointer.cptr(), pointer.length(),
636 mmap_obj.isWritable() ? ReadOnly::ReadWrite : ReadOnly::ReadOnly));
637 result.setFormat(SmallStr::fromCodePoint('B'));
638 return *result;
639 }
640 // Handle a buffer protocol object. Ideally we would skip an intermediate
641 // copy, but for now we make one copy of the data into a bytes.
642 // TODO(T85440357): Point directly to the buffer protocol object or
643 // bufferinfo from the memoryview.
644 Object bytes(&scope, newBytesFromBuffer(thread, object));
645 if (bytes.isError()) {
646 return *bytes;
647 }
648 return runtime->newMemoryView(
649 thread, object, bytes, Bytes::cast(*bytes).length(), ReadOnly::ReadOnly);
650}
651
652RawObject METH(memoryview, tobytes)(Thread* thread, Arguments args) {
653 HandleScope scope(thread);
654 Object self_obj(&scope, args.get(0));
655 if (!self_obj.isMemoryView()) {
656 return thread->raiseRequiresType(self_obj, ID(memoryview));
657 }
658 MemoryView self(&scope, *self_obj);
659 word length = self.length();
660 Runtime* runtime = thread->runtime();
661 Object buffer(&scope, self.buffer());
662 if (runtime->isInstanceOfBytes(*buffer)) {
663 MutableBytes result(&scope, runtime->newMutableBytesUninitialized(length));
664 Bytes bytes(&scope, *buffer);
665 result.replaceFromWithBytes(0, *bytes, length);
666 return result.becomeImmutable();
667 }
668 DCHECK(buffer.isPointer(), "memoryview.__getitem__ with non bytes/memory");
669 void* cptr = Pointer::cast(*buffer).cptr();
670 DCHECK(length <= Pointer::cast(*buffer).length(), "invalid length");
671 return runtime->newBytesWithAll(View<byte>(static_cast<byte*>(cptr), length));
672}
673
674} // namespace py