this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "bytearray-builtins.h"
3
4#include "builtins.h"
5#include "bytes-builtins.h"
6#include "byteslike.h"
7#include "formatter-utils.h"
8#include "int-builtins.h"
9#include "runtime.h"
10#include "slice-builtins.h"
11#include "type-builtins.h"
12#include "unicode.h"
13
14namespace py {
15
16RawObject bytearrayAsBytes(Thread* thread, const Bytearray& array) {
17 HandleScope scope(thread);
18 Bytes bytes(&scope, array.items());
19 return bytesSubseq(thread, bytes, 0, array.numItems());
20}
21
22static const BuiltinAttribute kBytearrayAttributes[] = {
23 {ID(_bytearray__bytes), RawBytearray::kItemsOffset,
24 AttributeFlags::kHidden},
25 {ID(_bytearray__num_items), RawBytearray::kNumItemsOffset,
26 AttributeFlags::kHidden},
27};
28
29static const BuiltinAttribute kBytearrayIteratorAttributes[] = {
30 {ID(_bytearray_iterator__iterable), RawBytearrayIterator::kIterableOffset,
31 AttributeFlags::kHidden},
32 {ID(_bytearray_iterator__index), RawBytearrayIterator::kIndexOffset,
33 AttributeFlags::kHidden},
34};
35
36void initializeBytearrayTypes(Thread* thread) {
37 addBuiltinType(thread, ID(bytearray), LayoutId::kBytearray,
38 /*superclass_id=*/LayoutId::kObject, kBytearrayAttributes,
39 Bytearray::kSize, /*basetype=*/true);
40
41 addBuiltinType(thread, ID(bytearray_iterator), LayoutId::kBytearrayIterator,
42 /*superclass_id=*/LayoutId::kObject,
43 kBytearrayIteratorAttributes, BytearrayIterator::kSize,
44 /*basetype=*/false);
45}
46
47RawObject METH(bytearray, __add__)(Thread* thread, Arguments args) {
48 HandleScope scope(thread);
49 Object self_obj(&scope, args.get(0));
50 Runtime* runtime = thread->runtime();
51 if (!runtime->isInstanceOfBytearray(*self_obj)) {
52 return thread->raiseRequiresType(self_obj, ID(bytearray));
53 }
54 Object other_obj(&scope, args.get(1));
55 word other_len;
56 if (runtime->isInstanceOfBytearray(*other_obj)) {
57 Bytearray array(&scope, *other_obj);
58 other_len = array.numItems();
59 other_obj = array.items();
60 } else if (runtime->isInstanceOfBytes(*other_obj)) {
61 Bytes bytes(&scope, bytesUnderlying(*other_obj));
62 other_len = bytes.length();
63 other_obj = *bytes;
64 } else {
65 return thread->raiseWithFmt(
66 LayoutId::kTypeError,
67 "can only concatenate bytearray or bytes to bytearray");
68 }
69
70 Bytearray self(&scope, *self_obj);
71 Bytes self_bytes(&scope, self.items());
72 word self_len = self.numItems();
73 Bytes other_bytes(&scope, *other_obj);
74
75 Bytearray result(&scope, runtime->newBytearray());
76 runtime->bytearrayEnsureCapacity(thread, result, self_len + other_len);
77 runtime->bytearrayIadd(thread, result, self_bytes, self_len);
78 runtime->bytearrayIadd(thread, result, other_bytes, other_len);
79 return *result;
80}
81
82RawObject METH(bytearray, __eq__)(Thread* thread, Arguments args) {
83 Runtime* runtime = thread->runtime();
84 HandleScope scope(thread);
85 Object self_obj(&scope, args.get(0));
86 if (!runtime->isInstanceOfBytearray(*self_obj)) {
87 return thread->raiseRequiresType(self_obj, ID(bytearray));
88 }
89 Bytearray self(&scope, *self_obj);
90 Object other_obj(&scope, args.get(1));
91 word comparison;
92 if (runtime->isInstanceOfBytes(*other_obj)) {
93 Bytes other(&scope, bytesUnderlying(*other_obj));
94 comparison = self.compare(*other, other.length());
95 } else if (runtime->isInstanceOfBytearray(*other_obj)) {
96 Bytearray other(&scope, *other_obj);
97 Bytes other_bytes(&scope, other.items());
98 comparison = self.compare(*other_bytes, other.numItems());
99 } else {
100 // TODO(T38246066): allow any bytes-like object
101 return NotImplementedType::object();
102 }
103 return Bool::fromBool(comparison == 0);
104}
105
106RawObject METH(bytearray, __ge__)(Thread* thread, Arguments args) {
107 Runtime* runtime = thread->runtime();
108 HandleScope scope(thread);
109 Object self_obj(&scope, args.get(0));
110 if (!runtime->isInstanceOfBytearray(*self_obj)) {
111 return thread->raiseRequiresType(self_obj, ID(bytearray));
112 }
113 Bytearray self(&scope, *self_obj);
114 Object other_obj(&scope, args.get(1));
115 word comparison;
116 if (runtime->isInstanceOfBytes(*other_obj)) {
117 Bytes other(&scope, bytesUnderlying(*other_obj));
118 comparison = self.compare(*other, other.length());
119 } else if (runtime->isInstanceOfBytearray(*other_obj)) {
120 Bytearray other(&scope, *other_obj);
121 Bytes other_bytes(&scope, other.items());
122 comparison = self.compare(*other_bytes, other.numItems());
123 } else {
124 // TODO(T38246066): allow any bytes-like object
125 return NotImplementedType::object();
126 }
127 return Bool::fromBool(comparison >= 0);
128}
129
130RawObject METH(bytearray, __gt__)(Thread* thread, Arguments args) {
131 Runtime* runtime = thread->runtime();
132 HandleScope scope(thread);
133 Object self_obj(&scope, args.get(0));
134 if (!runtime->isInstanceOfBytearray(*self_obj)) {
135 return thread->raiseRequiresType(self_obj, ID(bytearray));
136 }
137 Bytearray self(&scope, *self_obj);
138 Object other_obj(&scope, args.get(1));
139 word comparison;
140 if (runtime->isInstanceOfBytes(*other_obj)) {
141 Bytes other(&scope, bytesUnderlying(*other_obj));
142 comparison = self.compare(*other, other.length());
143 } else if (runtime->isInstanceOfBytearray(*other_obj)) {
144 Bytearray other(&scope, *other_obj);
145 Bytes other_bytes(&scope, other.items());
146 comparison = self.compare(*other_bytes, other.numItems());
147 } else {
148 // TODO(T38246066): allow any bytes-like object
149 return NotImplementedType::object();
150 }
151 return Bool::fromBool(comparison > 0);
152}
153
154RawObject METH(bytearray, __iadd__)(Thread* thread, Arguments args) {
155 HandleScope scope(thread);
156 Object self_obj(&scope, args.get(0));
157 Runtime* runtime = thread->runtime();
158 if (!runtime->isInstanceOfBytearray(*self_obj)) {
159 return thread->raiseRequiresType(self_obj, ID(bytearray));
160 }
161 Bytearray self(&scope, *self_obj);
162
163 Object other_obj(&scope, args.get(1));
164 Byteslike other(&scope, thread, *other_obj);
165 if (!other.isValid()) {
166 return thread->raiseWithFmt(LayoutId::kTypeError, "can't concat %T to %T",
167 &other_obj, &self);
168 }
169
170 word num_items = self.numItems();
171 word other_length = other.length();
172 word new_length = num_items + other_length;
173 runtime->bytearrayEnsureCapacity(thread, self, new_length);
174 MutableBytes::cast(self.items())
175 .replaceFromWithByteslike(num_items, other, other_length);
176 self.setNumItems(new_length);
177 return *self;
178}
179
180RawObject METH(bytearray, __imul__)(Thread* thread, Arguments args) {
181 Runtime* runtime = thread->runtime();
182 HandleScope scope(thread);
183 Object self_obj(&scope, args.get(0));
184 if (!runtime->isInstanceOfBytearray(*self_obj)) {
185 return thread->raiseRequiresType(self_obj, ID(bytearray));
186 }
187 Bytearray self(&scope, *self_obj);
188 Object count_index(&scope, args.get(1));
189 Object count_obj(&scope, intFromIndex(thread, count_index));
190 if (count_obj.isError()) return *count_obj;
191 word count = intUnderlying(*count_obj).asWordSaturated();
192 if (!SmallInt::isValid(count)) {
193 return thread->raiseWithFmt(LayoutId::kOverflowError,
194 "cannot fit '%T' into an index-sized integer",
195 &count_index);
196 }
197 if (count == 1) {
198 return *self;
199 }
200 word length = self.numItems();
201 if (count <= 0 || length == 0) {
202 self.downsize(0);
203 return *self;
204 }
205 word new_length;
206 if (__builtin_mul_overflow(length, count, &new_length) ||
207 !SmallInt::isValid(new_length)) {
208 return thread->raiseMemoryError();
209 }
210 Bytes source(&scope, self.items());
211 if (new_length <= self.capacity()) {
212 // fits into existing backing LargeBytes - repeat in place
213 for (word i = 1; i < count; i++) {
214 runtime->bytearrayIadd(thread, self, source, length);
215 }
216 return *self;
217 }
218 // grows beyond existing bytes - allocate new
219 self.setItems(runtime->bytesRepeat(thread, source, length, count));
220 DCHECK(self.capacity() == new_length, "unexpected result length");
221 self.setNumItems(new_length);
222 return *self;
223}
224
225RawObject METH(bytearray, __iter__)(Thread* thread, Arguments args) {
226 HandleScope scope(thread);
227 Object self_obj(&scope, args.get(0));
228 Runtime* runtime = thread->runtime();
229 if (!runtime->isInstanceOfBytearray(*self_obj)) {
230 return thread->raiseRequiresType(self_obj, ID(bytearray));
231 }
232 Bytearray self(&scope, *self_obj);
233 return runtime->newBytearrayIterator(thread, self);
234}
235
236RawObject METH(bytearray, __le__)(Thread* thread, Arguments args) {
237 Runtime* runtime = thread->runtime();
238 HandleScope scope(thread);
239 Object self_obj(&scope, args.get(0));
240 if (!runtime->isInstanceOfBytearray(*self_obj)) {
241 return thread->raiseRequiresType(self_obj, ID(bytearray));
242 }
243 Bytearray self(&scope, *self_obj);
244 Object other_obj(&scope, args.get(1));
245 word comparison;
246 if (runtime->isInstanceOfBytes(*other_obj)) {
247 Bytes other(&scope, bytesUnderlying(*other_obj));
248 comparison = self.compare(*other, other.length());
249 } else if (runtime->isInstanceOfBytearray(*other_obj)) {
250 Bytearray other(&scope, *other_obj);
251 Bytes other_bytes(&scope, other.items());
252 comparison = self.compare(*other_bytes, other.numItems());
253 } else {
254 // TODO(T38246066): allow any bytes-like object
255 return NotImplementedType::object();
256 }
257 return Bool::fromBool(comparison <= 0);
258}
259
260RawObject METH(bytearray, __len__)(Thread* thread, Arguments args) {
261 HandleScope scope(thread);
262 Object self_obj(&scope, args.get(0));
263 if (!thread->runtime()->isInstanceOfBytearray(*self_obj)) {
264 return thread->raiseRequiresType(self_obj, ID(bytearray));
265 }
266 Bytearray self(&scope, *self_obj);
267 return SmallInt::fromWord(self.numItems());
268}
269
270RawObject METH(bytearray, __lt__)(Thread* thread, Arguments args) {
271 Runtime* runtime = thread->runtime();
272 HandleScope scope(thread);
273 Object self_obj(&scope, args.get(0));
274 if (!runtime->isInstanceOfBytearray(*self_obj)) {
275 return thread->raiseRequiresType(self_obj, ID(bytearray));
276 }
277 Bytearray self(&scope, *self_obj);
278 Object other_obj(&scope, args.get(1));
279 word comparison;
280 if (runtime->isInstanceOfBytes(*other_obj)) {
281 Bytes other(&scope, bytesUnderlying(*other_obj));
282 comparison = self.compare(*other, other.length());
283 } else if (runtime->isInstanceOfBytearray(*other_obj)) {
284 Bytearray other(&scope, *other_obj);
285 Bytes other_bytes(&scope, other.items());
286 comparison = self.compare(*other_bytes, other.numItems());
287 } else {
288 // TODO(T38246066): allow any bytes-like object
289 return NotImplementedType::object();
290 }
291 return Bool::fromBool(comparison < 0);
292}
293
294RawObject METH(bytearray, __mul__)(Thread* thread, Arguments args) {
295 Runtime* runtime = thread->runtime();
296 HandleScope scope(thread);
297 Object self_obj(&scope, args.get(0));
298 if (!runtime->isInstanceOfBytearray(*self_obj)) {
299 return thread->raiseRequiresType(self_obj, ID(bytearray));
300 }
301 Bytearray self(&scope, *self_obj);
302 Object count_index(&scope, args.get(1));
303 Object count_obj(&scope, intFromIndex(thread, count_index));
304 if (count_obj.isError()) return *count_obj;
305 word count = intUnderlying(*count_obj).asWordSaturated();
306 if (!SmallInt::isValid(count)) {
307 return thread->raiseWithFmt(LayoutId::kOverflowError,
308 "cannot fit '%T' into an index-sized integer",
309 &count_index);
310 }
311 word length = self.numItems();
312 if (count <= 0 || length == 0) {
313 return runtime->newBytearray();
314 }
315 word new_length;
316 if (__builtin_mul_overflow(length, count, &new_length) ||
317 !SmallInt::isValid(new_length)) {
318 return thread->raiseMemoryError();
319 }
320 Bytes source(&scope, self.items());
321 Bytearray result(&scope, runtime->newBytearray());
322 Bytes repeated(&scope, runtime->bytesRepeat(thread, source, length, count));
323 DCHECK(repeated.length() == new_length, "unexpected result length");
324 if (repeated.isSmallBytes()) {
325 runtime->bytearrayIadd(thread, result, repeated, new_length);
326 } else {
327 result.setItems(*repeated);
328 result.setNumItems(new_length);
329 }
330 return *result;
331}
332
333RawObject METH(bytearray, __ne__)(Thread* thread, Arguments args) {
334 Runtime* runtime = thread->runtime();
335 HandleScope scope(thread);
336 Object self_obj(&scope, args.get(0));
337 if (!runtime->isInstanceOfBytearray(*self_obj)) {
338 return thread->raiseRequiresType(self_obj, ID(bytearray));
339 }
340 Bytearray self(&scope, *self_obj);
341 Object other_obj(&scope, args.get(1));
342 word comparison;
343 if (runtime->isInstanceOfBytes(*other_obj)) {
344 Bytes other(&scope, bytesUnderlying(*other_obj));
345 comparison = self.compare(*other, other.length());
346 } else if (runtime->isInstanceOfBytearray(*other_obj)) {
347 Bytearray other(&scope, *other_obj);
348 Bytes other_bytes(&scope, other.items());
349 comparison = self.compare(*other_bytes, other.numItems());
350 } else {
351 // TODO(T38246066): allow any bytes-like object
352 return NotImplementedType::object();
353 }
354 return Bool::fromBool(comparison != 0);
355}
356
357RawObject METH(bytearray, __new__)(Thread* thread, Arguments args) {
358 HandleScope scope(thread);
359 Object type_obj(&scope, args.get(0));
360 Runtime* runtime = thread->runtime();
361 if (!runtime->isInstanceOfType(*type_obj)) {
362 return thread->raiseWithFmt(LayoutId::kTypeError, "not a type object");
363 }
364 Type type(&scope, *type_obj);
365 if (type.builtinBase() != LayoutId::kBytearray) {
366 return thread->raiseWithFmt(LayoutId::kTypeError,
367 "not a subtype of bytearray");
368 }
369 Layout layout(&scope, type.instanceLayout());
370 Bytearray result(&scope, runtime->newInstance(layout));
371 result.setItems(runtime->emptyMutableBytes());
372 result.setNumItems(0);
373 return *result;
374}
375
376RawObject bytearrayRepr(Thread* thread, const Bytearray& array) {
377 HandleScope scope(thread);
378 Runtime* runtime = thread->runtime();
379 Type cls(&scope, runtime->typeOf(*array));
380 Str name(&scope, cls.name());
381 word name_length = name.length();
382 word length = array.numItems();
383 if (length > (kMaxWord - 6 - name_length) / 4) {
384 return thread->raiseWithFmt(LayoutId::kOverflowError,
385 "bytearray object is too large to make repr");
386 }
387
388 // Precalculate length and determine which quote to use; single is preferred
389 word result_length = name_length + length + 5; // <cls>(b'<contents>')
390 bool has_single_quote = false;
391 bool has_double_quote = false;
392 for (word i = 0; i < length; i++) {
393 byte current = array.byteAt(i);
394 switch (current) {
395 case '\'':
396 result_length++;
397 has_single_quote = true;
398 break;
399 case '"':
400 has_double_quote = true;
401 break;
402 case '\t':
403 case '\n':
404 case '\r':
405 case '\\':
406 result_length++;
407 break;
408 default:
409 if (!ASCII::isPrintable(current)) {
410 result_length += 3;
411 }
412 }
413 }
414 byte delimiter = (has_single_quote && !has_double_quote) ? '"' : '\'';
415
416 MutableBytes result(&scope,
417 runtime->newMutableBytesUninitialized(result_length));
418 word j = 0;
419 result.replaceFromWithStr(0, *name, name_length);
420 j += name_length;
421 result.byteAtPut(j++, '(');
422 result.byteAtPut(j++, 'b');
423 result.byteAtPut(j++, delimiter);
424
425 for (word i = 0; i < length; i++) {
426 byte current = array.byteAt(i);
427 switch (current) {
428 case '\'':
429 result.byteAtPut(j++, '\\');
430 result.byteAtPut(j++, current);
431 break;
432 case '\t':
433 result.byteAtPut(j++, '\\');
434 result.byteAtPut(j++, 't');
435 break;
436 case '\n':
437 result.byteAtPut(j++, '\\');
438 result.byteAtPut(j++, 'n');
439 break;
440 case '\r':
441 result.byteAtPut(j++, '\\');
442 result.byteAtPut(j++, 'r');
443 break;
444 case '\\':
445 result.byteAtPut(j++, '\\');
446 result.byteAtPut(j++, '\\');
447 break;
448 default:
449 if (ASCII::isPrintable(current)) {
450 result.byteAtPut(j++, current);
451 } else {
452 result.byteAtPut(j++, '\\');
453 result.byteAtPut(j++, 'x');
454 uwordToHexadecimalWithMutableBytes(*result, /*index=*/j,
455 /*num_digits=*/2, current);
456 j += 2;
457 }
458 }
459 }
460
461 result.byteAtPut(j++, delimiter);
462 result.byteAtPut(j++, ')');
463 DCHECK(j == result_length, "expected %ld bytes, wrote %ld", result_length, j);
464 return result.becomeStr();
465}
466
467RawObject METH(bytearray, __repr__)(Thread* thread, Arguments args) {
468 HandleScope scope(thread);
469 Object self_obj(&scope, args.get(0));
470 Runtime* runtime = thread->runtime();
471 if (!runtime->isInstanceOfBytearray(*self_obj)) {
472 return thread->raiseRequiresType(self_obj, ID(bytearray));
473 }
474 Bytearray self(&scope, *self_obj);
475 return bytearrayRepr(thread, self);
476}
477
478RawObject METH(bytearray, hex)(Thread* thread, Arguments args) {
479 HandleScope scope(thread);
480 Object obj(&scope, args.get(0));
481 if (!thread->runtime()->isInstanceOfBytearray(*obj)) {
482 return thread->raiseRequiresType(obj, ID(bytearray));
483 }
484 Bytearray self(&scope, *obj);
485 Bytes bytes(&scope, self.items());
486 return bytesHex(thread, bytes, self.numItems());
487}
488
489RawObject METH(bytearray, lower)(Thread* thread, Arguments args) {
490 HandleScope scope(thread);
491 Object self_obj(&scope, args.get(0));
492 Runtime* runtime = thread->runtime();
493 if (!runtime->isInstanceOfBytearray(*self_obj)) {
494 return thread->raiseRequiresType(self_obj, ID(bytearray));
495 }
496 Bytearray self(&scope, *self_obj);
497 Bytes items(&scope, self.items());
498 word num_items = self.numItems();
499 MutableBytes lowered(&scope,
500 runtime->newMutableBytesUninitialized(items.length()));
501 for (word i = 0; i < num_items; i++) {
502 lowered.byteAtPut(i, ASCII::toLower(items.byteAt(i)));
503 }
504 Bytearray result(&scope, runtime->newBytearray());
505 result.setItems(*lowered);
506 result.setNumItems(num_items);
507 return *result;
508}
509
510RawObject METH(bytearray, lstrip)(Thread* thread, Arguments args) {
511 HandleScope scope(thread);
512 Object self_obj(&scope, args.get(0));
513 Runtime* runtime = thread->runtime();
514 if (!runtime->isInstanceOfBytearray(*self_obj)) {
515 return thread->raiseRequiresType(self_obj, ID(bytearray));
516 }
517 Bytearray self(&scope, *self_obj);
518 Bytes self_bytes(&scope, self.items());
519 Object chars_obj(&scope, args.get(1));
520 Bytes result_bytes(&scope, Bytes::empty());
521 if (chars_obj.isNoneType()) {
522 result_bytes = bytesStripSpaceLeft(thread, self_bytes, self.numItems());
523 } else if (runtime->isInstanceOfBytes(*chars_obj)) {
524 Bytes chars(&scope, bytesUnderlying(*chars_obj));
525 result_bytes = bytesStripLeft(thread, self_bytes, self.numItems(), chars,
526 chars.length());
527 } else if (runtime->isInstanceOfBytearray(*chars_obj)) {
528 Bytearray chars(&scope, *chars_obj);
529 Bytes chars_bytes(&scope, chars.items());
530 result_bytes = bytesStripLeft(thread, self_bytes, self.numItems(),
531 chars_bytes, chars.numItems());
532 } else {
533 // TODO(T38246066): support bytes-like objects other than bytes, bytearray
534 return thread->raiseWithFmt(LayoutId::kTypeError,
535 "a bytes-like object is required, not '%T'",
536 &chars_obj);
537 }
538 Bytearray result(&scope, runtime->newBytearray());
539 runtime->bytearrayIadd(thread, result, result_bytes, result_bytes.length());
540 return *result;
541}
542
543RawObject METH(bytearray, rstrip)(Thread* thread, Arguments args) {
544 HandleScope scope(thread);
545 Object self_obj(&scope, args.get(0));
546 Runtime* runtime = thread->runtime();
547 if (!runtime->isInstanceOfBytearray(*self_obj)) {
548 return thread->raiseRequiresType(self_obj, ID(bytearray));
549 }
550 Bytearray self(&scope, *self_obj);
551 Bytes self_bytes(&scope, self.items());
552 Object chars_obj(&scope, args.get(1));
553 Bytes result_bytes(&scope, Bytes::empty());
554 if (chars_obj.isNoneType()) {
555 result_bytes = bytesStripSpaceRight(thread, self_bytes, self.numItems());
556 } else if (runtime->isInstanceOfBytes(*chars_obj)) {
557 Bytes chars(&scope, bytesUnderlying(*chars_obj));
558 result_bytes = bytesStripRight(thread, self_bytes, self.numItems(), chars,
559 chars.length());
560 } else if (runtime->isInstanceOfBytearray(*chars_obj)) {
561 Bytearray chars(&scope, *chars_obj);
562 Bytes chars_bytes(&scope, chars.items());
563 result_bytes = bytesStripRight(thread, self_bytes, self.numItems(),
564 chars_bytes, chars.numItems());
565 } else {
566 // TODO(T38246066): support bytes-like objects other than bytes, bytearray
567 return thread->raiseWithFmt(LayoutId::kTypeError,
568 "a bytes-like object is required, not '%T'",
569 &chars_obj);
570 }
571 Bytearray result(&scope, runtime->newBytearray());
572 runtime->bytearrayIadd(thread, result, result_bytes, result_bytes.length());
573 return *result;
574}
575
576RawObject METH(bytearray, strip)(Thread* thread, Arguments args) {
577 HandleScope scope(thread);
578 Object self_obj(&scope, args.get(0));
579 Runtime* runtime = thread->runtime();
580 if (!runtime->isInstanceOfBytearray(*self_obj)) {
581 return thread->raiseRequiresType(self_obj, ID(bytearray));
582 }
583 Bytearray self(&scope, *self_obj);
584 Bytes self_bytes(&scope, self.items());
585 Object chars_obj(&scope, args.get(1));
586 Bytes result_bytes(&scope, Bytes::empty());
587 if (chars_obj.isNoneType()) {
588 result_bytes = bytesStripSpace(thread, self_bytes, self.numItems());
589 } else if (runtime->isInstanceOfBytes(*chars_obj)) {
590 Bytes chars(&scope, bytesUnderlying(*chars_obj));
591 result_bytes =
592 bytesStrip(thread, self_bytes, self.numItems(), chars, chars.length());
593 } else if (runtime->isInstanceOfBytearray(*chars_obj)) {
594 Bytearray chars(&scope, *chars_obj);
595 Bytes chars_bytes(&scope, chars.items());
596 result_bytes = bytesStrip(thread, self_bytes, self.numItems(), chars_bytes,
597 chars.numItems());
598 } else {
599 // TODO(T38246066): support bytes-like objects other than bytes, bytearray
600 return thread->raiseWithFmt(LayoutId::kTypeError,
601 "a bytes-like object is required, not '%T'",
602 &chars_obj);
603 }
604 Bytearray result(&scope, runtime->newBytearray());
605 runtime->bytearrayIadd(thread, result, result_bytes, result_bytes.length());
606 return *result;
607}
608
609RawObject METH(bytearray, translate)(Thread* thread, Arguments args) {
610 HandleScope scope(thread);
611 Object self_obj(&scope, args.get(0));
612 Runtime* runtime = thread->runtime();
613 if (!runtime->isInstanceOfBytearray(*self_obj)) {
614 return thread->raiseRequiresType(self_obj, ID(bytearray));
615 }
616 Bytearray self(&scope, *self_obj);
617 Bytes self_bytes(&scope, self.items());
618 Object table_obj(&scope, args.get(1));
619 word table_length;
620 if (table_obj.isNoneType()) {
621 table_length = kByteTranslationTableLength;
622 table_obj = Bytes::empty();
623 } else if (runtime->isInstanceOfBytes(*table_obj)) {
624 Bytes bytes(&scope, bytesUnderlying(*table_obj));
625 table_length = bytes.length();
626 table_obj = *bytes;
627 } else if (runtime->isInstanceOfBytearray(*table_obj)) {
628 Bytearray array(&scope, *table_obj);
629 table_length = array.numItems();
630 table_obj = array.items();
631 } else {
632 // TODO(T38246066): allow any bytes-like object
633 return thread->raiseWithFmt(LayoutId::kTypeError,
634 "a bytes-like object is required, not '%T'",
635 &table_obj);
636 }
637 if (table_length != kByteTranslationTableLength) {
638 return thread->raiseWithFmt(LayoutId::kValueError,
639 "translation table must be %w characters long",
640 kByteTranslationTableLength);
641 }
642 Bytes table(&scope, *table_obj);
643 Object del(&scope, args.get(2));
644 Bytes translated(&scope, Bytes::empty());
645 if (runtime->isInstanceOfBytes(*del)) {
646 Bytes bytes(&scope, bytesUnderlying(*del));
647 translated =
648 runtime->bytesTranslate(thread, self_bytes, self.numItems(), table,
649 table_length, bytes, bytes.length());
650 } else if (runtime->isInstanceOfBytearray(*del)) {
651 Bytearray array(&scope, *del);
652 Bytes bytes(&scope, array.items());
653 translated =
654 runtime->bytesTranslate(thread, self_bytes, self.numItems(), table,
655 table_length, bytes, array.numItems());
656 } else {
657 // TODO(T38246066): allow any bytes-like object
658 return thread->raiseWithFmt(LayoutId::kTypeError,
659 "a bytes-like object is required, not '%T'",
660 &del);
661 }
662 Bytearray result(&scope, runtime->newBytearray());
663 if (translated.isSmallBytes()) {
664 runtime->bytearrayIadd(thread, result, translated, translated.length());
665 } else {
666 result.setItems(*translated);
667 result.setNumItems(translated.length());
668 }
669 return *result;
670}
671
672RawObject METH(bytearray, upper)(Thread* thread, Arguments args) {
673 HandleScope scope(thread);
674 Object self_obj(&scope, args.get(0));
675 Runtime* runtime = thread->runtime();
676 if (!runtime->isInstanceOfBytearray(*self_obj)) {
677 return thread->raiseRequiresType(self_obj, ID(bytearray));
678 }
679 Bytearray self(&scope, *self_obj);
680 Bytes items(&scope, self.items());
681 word num_items = self.numItems();
682 MutableBytes uppered(&scope,
683 runtime->newMutableBytesUninitialized(items.length()));
684 for (word i = 0; i < num_items; i++) {
685 uppered.byteAtPut(i, ASCII::toUpper(items.byteAt(i)));
686 }
687 Bytearray result(&scope, runtime->newBytearray());
688 result.setItems(*uppered);
689 result.setNumItems(num_items);
690 return *result;
691}
692
693static RawObject bytearraySplitLines(Thread* thread, const Bytearray& bytearray,
694 bool keepends) {
695 HandleScope scope(thread);
696 Runtime* runtime = thread->runtime();
697 List result(&scope, runtime->newList());
698 word length = bytearray.numItems();
699 Bytearray line(&scope, *bytearray);
700
701 for (word i = 0, j = 0; i < length; j = i) {
702 // Skip newline bytes
703 for (; i < length; i++) {
704 byte b = bytearray.byteAt(i);
705 if (b == '\n' || b == '\r') {
706 break;
707 }
708 }
709
710 word eol_pos = i;
711 if (i < length) {
712 word cur = i;
713 word next = i + 1;
714 i++;
715 // Check for \r\n specifically
716 if (bytearray.byteAt(cur) == '\r' && next < length &&
717 bytearray.byteAt(next) == '\n') {
718 i++;
719 }
720 if (keepends) {
721 eol_pos = i;
722 }
723 }
724
725 line = runtime->newBytearray();
726 word line_length = eol_pos - j;
727 runtime->bytearrayEnsureCapacity(thread, line, line_length);
728 line.setNumItems(line_length);
729 line.replaceFromWithStartAt(0, *bytearray, line_length, j);
730
731 runtime->listAdd(thread, result, line);
732 }
733
734 return *result;
735}
736
737RawObject METH(bytearray, splitlines)(Thread* thread, Arguments args) {
738 HandleScope scope(thread);
739 Runtime* runtime = thread->runtime();
740 Object self_obj(&scope, args.get(0));
741 Object keepends_obj(&scope, args.get(1));
742 if (!runtime->isInstanceOfBytearray(*self_obj)) {
743 return thread->raiseRequiresType(self_obj, ID(bytearray));
744 }
745 if (!runtime->isInstanceOfInt(*keepends_obj)) {
746 return thread->raiseRequiresType(keepends_obj, ID(int));
747 }
748 Bytearray self(&scope, *self_obj);
749 bool keepends = !intUnderlying(*keepends_obj).isZero();
750 return bytearraySplitLines(thread, self, keepends);
751}
752
753RawObject METH(bytearray_iterator, __iter__)(Thread* thread, Arguments args) {
754 HandleScope scope(thread);
755 Object self(&scope, args.get(0));
756 if (!self.isBytearrayIterator()) {
757 return thread->raiseRequiresType(self, ID(bytearray_iterator));
758 }
759 return *self;
760}
761
762RawObject METH(bytearray_iterator, __next__)(Thread* thread, Arguments args) {
763 HandleScope scope(thread);
764 Object self_obj(&scope, args.get(0));
765 if (!self_obj.isBytearrayIterator()) {
766 return thread->raiseRequiresType(self_obj, ID(bytearray_iterator));
767 }
768 BytearrayIterator self(&scope, *self_obj);
769 Bytearray bytearray(&scope, self.iterable());
770 if (self.index() >= bytearray.numItems()) {
771 return thread->raise(LayoutId::kStopIteration, NoneType::object());
772 }
773 Int item(&scope, thread->runtime()->newInt(bytearray.byteAt(self.index())));
774 self.setIndex(self.index() + 1);
775 return *item;
776}
777
778RawObject METH(bytearray_iterator, __length_hint__)(Thread* thread,
779 Arguments args) {
780 HandleScope scope(thread);
781 Object self_obj(&scope, args.get(0));
782 if (!self_obj.isBytearrayIterator()) {
783 return thread->raiseRequiresType(self_obj, ID(bytearray_iterator));
784 }
785 BytearrayIterator self(&scope, *self_obj);
786 Bytearray bytearray(&scope, self.iterable());
787 return SmallInt::fromWord(bytearray.numItems() - self.index());
788}
789
790} // namespace py