10000 Add time capsule to store strings between supervisor reloads by TG-Techie · Pull Request #4597 · adafruit/circuitpython · GitHub
[go: up one dir, main page]

Skip to content

Add time capsule to store strings between supervisor reloads #4597

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 10 additions & 16 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1084,10 +1084,6 @@ msgstr ""
msgid "I2SOut not available"
msgstr ""

#: ports/esp32s2/common-hal/alarm/pin/__init__.c
msgid "IOs 0, 2 & 4 do not support internal pullup in sleep"
msgstr ""

#: shared-bindings/aesio/aes.c
#, c-format
msgid "IV must be %d bytes long"
Expand Down Expand Up @@ -1601,6 +1597,7 @@ msgstr ""
msgid "Not a valid IP string"
msgstr ""

#: ports/nrf/common-hal/_bleio/PacketBuffer.c
#: ports/nrf/common-hal/_bleio/__init__.c
#: shared-bindings/_bleio/CharacteristicBuffer.c
msgid "Not connected"
Expand Down Expand Up @@ -2563,6 +2560,10 @@ msgstr ""
msgid "can only have up to 4 parameters to Xtensa assembly"
msgstr ""

#: shared-bindings/capsuleio/__init__.c
msgid "can only save a string or None in the time capsule"
msgstr ""

#: py/persistentcode.c
msgid "can only save bytecode"
msgstr ""
Expand Down Expand Up @@ -3253,10 +3254,6 @@ msgstr ""
msgid "invalid syntax for number"
msgstr ""

#: ports/esp32s2/common-hal/alarm/pin/__init__.c
msgid "io must be rtc io"
msgstr ""

#: py/objtype.c
msgid "issubclass() arg 1 must be a class"
msgstr ""
Expand Down Expand Up @@ -3716,6 +3713,7 @@ msgstr ""
#: ports/esp32s2/boards/adafruit_funhouse/mpconfigboard.h
#: ports/esp32s2/boards/adafruit_magtag_2.9_grayscale/mpconfigboard.h
#: ports/esp32s2/boards/adafruit_metro_esp32s2/mpconfigboard.h
#: ports/esp32s2/boards/artisense_rd00/mpconfigboard.h
#: ports/esp32s2/boards/electroniccats_bastwifi/mpconfigboard.h
#: ports/esp32s2/boards/espressif_kaluga_1/mpconfigboard.h
#: ports/esp32s2/boards/espressif_saola_1_wroom/mpconfigboard.h
Expand Down Expand Up @@ -3989,6 +3987,10 @@ msgstr ""
msgid "tobytes can be invoked for dense arrays only"
msgstr ""

#: shared-bindings/capsuleio/__init__.c
msgid "too long to store in time capsule"
msgstr ""

#: shared-module/struct/__init__.c
msgid "too many arguments provided with the given format"
msgstr ""
Expand All @@ -4014,10 +4016,6 @@ msgstr ""
msgid "trapz is defined for 1D arrays of equal length"
msgstr ""

#: ports/esp32s2/common-hal/alarm/pin/__init__.c
msgid "trigger level must be 0 or 1"
msgstr ""

#: py/obj.c
msgid "tuple/list has wrong length"
msgstr ""
Expand Down Expand Up @@ -4152,10 +4150,6 @@ msgstr ""
msgid "value_count must be > 0"
msgstr ""

#: ports/esp32s2/common-hal/alarm/pin/__init__.c
msgid "wakeup conflict"
msgstr ""

#: ports/esp32s2/common-hal/watchdog/WatchDogTimer.c
msgid "watchdog not initialized"
msgstr ""
Expand Down
6 changes: 6 additions & 0 deletions locale/es.po
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,12 @@ msgstr "Valor máximo de x cuando se refleja es %d"
msgid "Messages limited to 8 bytes"
msgstr "Mensajes limitados a 8 bytes"

#: shared-bindings/capsuleio/__init__.c
msgid "too long to store in time capsule"
msgstr "demasiado largo para poner en la cápsula del tiempo"
msgid "can only save a string or None in the time capsule"
msgstr "sólo puedes guardar None o cadenas"

#: shared-bindings/audiobusio/PDMIn.c
msgid "Microphone startup delay must be in range 0.0 to 1.0"
msgstr "Micrófono demora de inicio debe estar en el rango 0.0 a 1.0"
Expand Down
2 changes: 2 additions & 0 deletions ports/nrf/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ CIRCUITPY_FRAMEBUFFERIO ?= 1
CIRCUITPY_COUNTIO = 0
CIRCUITPY_WATCHDOG ?= 1

CIRCUITPY_CAPSULEIO ?= 1

# nRF52840-specific

ifeq ($(MCU_CHIP),nrf52840)
Expand Down
4 changes: 4 additions & 0 deletions py/circuitpy_defns.mk
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ endif
ifeq ($(CIRCUITPY_CANIO),1)
SRC_PATTERNS += canio/%
endif
ifeq ($(CIRCUITPY_CAPSULEIO),1)
SRC_PATTERNS += capsuleio/%
endif
ifeq ($(CIRCUITPY_COUNTIO),1)
SRC_PATTERNS += countio/%
endif
Expand Down Expand Up @@ -485,6 +488,7 @@ SRC_SHARED_MODULE_ALL = \
canio/Match.c \
canio/Message.c \
canio/RemoteTransmissionRequest.c \
capsuleio/__init__.c \
displayio/Bitmap.c \
displayio/ColorConverter.c \
displayio/Display.c \
Expand Down
8 changes: 8 additions & 0 deletions py/circuitpy_mpconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,13 @@ extern const struct _mp_obj_module_t canio_module;
#define CANIO_MODULE
#endif

#if CIRCUITPY_CAPSULEIO
extern const struct _mp_obj_module_t capsuleio_module;
#define CAPSULEIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_capsuleio), (mp_obj_t)&capsuleio_module },
#else
#define CAPSULEIO_MODULE
#endif

#if CIRCUITPY_COUNTIO
extern const struct _mp_obj_module_t countio_module;
#define COUNTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_countio), (mp_obj_t)&countio_module },
Expand Down Expand Up @@ -850,6 +857,7 @@ extern const struct _mp_obj_module_t msgpack_module;
BUSIO_MODULE \
CAMERA_MODULE \
CANIO_MODULE \
CAPSULEIO_MODULE\
COUNTIO_MODULE \
DIGITALIO_MODULE \
DISPLAYIO_MODULE \
Expand Down
3 changes: 3 additions & 0 deletions py/circuitpy_mpconfig.mk
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ CFLAGS += -DCIRCUITPY_CAMERA=$(CIRCUITPY_CAMERA)
CIRCUITPY_CANIO ?= 0
CFLAGS += -DCIRCUITPY_CANIO=$(CIRCUITPY_CANIO)

CIRCUITPY_CAPSULEIO ?= 1
CFLAGS += -DCIRCUITPY_CAPSULEIO=$(CIRCUITPY_CAPSULEIO)

CIRCUITPY_DIGITALIO ?= 1
CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO)

Expand Down
65 changes: 65 additions & 0 deletions shared-bindings/capsuleio/__init__.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jonah Yolles-Murphy (TG-Techie)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

// TODO: add description of module

#include "py/objstr.h"
#include "py/runtime.h"
#include "shared-bindings/capsuleio/__init__.h"

// for now only string and none can be saved. in the future it may be tuples, floats, ints (maybe)
STATIC mp_obj_t capsule_bury(mp_obj_t obj) {
capsule_result_t result = capsuleio_bury_obj(obj);
switch (result) {
case CAPSULEIO_STRING_TO_LONG:
mp_raise_ValueError(translate("too long to store in time capsule"));
break; // is this needed? the above is noreturn
case CAPSULEIO_TYPE_CANNOT_BE_BURIED:
mp_raise_TypeError(translate("can only save a string or None in the time capsule"));
break; // is this needed? the above is noreturn
case CAPSULEIO_OK:
default:
return mp_const_none;
}
}

MP_DEFINE_CONST_FUN_OBJ_1(capsuleio_bury_fnobj, capsule_bury);
// this function requires no runtime wrapper
MP_DEFINE_CONST_FUN_OBJ_0(capsuleio_unearth_fnobj, capsuleio_unearth_new_obj);

STATIC const mp_rom_map_elem_t mp_module_capsuleio_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_capsuleio) },
{ MP_ROM_QSTR(MP_QSTR_bury), MP_ROM_PTR(&capsuleio_bury_fnobj) },
{ MP_ROM_QSTR(MP_QSTR_unearth), MP_ROM_PTR(&capsuleio_unearth_fnobj) },
};

STATIC MP_DEFINE_CONST_DICT(mp_module_capsuleio_globals, mp_module_capsuleio_globals_table);
< 10000 /td>

const mp_obj_module_t capsuleio_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mp_module_capsuleio_globals,
};
33 changes: 33 additions & 0 deletions shared-bindings/capsuleio/__init__.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jonah Yolles-Murphy (TG-Techie)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef MICROPY_INCLUDED_SHARED_BINDINGS_CAPSULEIO_H
#define MICROPY_INCLUDED_SHARED_BINDINGS_CAPSULEIO_H

#include "shared-module/capsuleio/__init__.h"


#endif // MICROPY_INCLUDED_SHARED_BINDINGS_CAPSULEIO_H
87 changes: 87 additions & 0 deletions shared-module/capsuleio/__init__.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Jonah Yolles-Murphy (TG-Techie)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "shared-bindings/capsuleio/__init__.h"
#include "py/objstr.h"
#include <string.h>


// the time capsule memory reservation
capsuleio_capsule_t capsuleio_capsule;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think think this is a reasonable thing to do. Have you considered such boards as the QT PY? This will take 1/32 of its total memory. Probably many folks are running those right on the edge and 1kb would push them over the edge and unable to update.

What does the data sheet say on typical circuitpython boards w.r.t. program/erase cycles on the embedded flash, and what’s the typical cycle count on express board flash? I imagine if you take into account whatever load leveling is there you’ll find that it’s expected to live decades or more with a 1kb text file written occasionally. (I.e., is this solving a real problem?)

Maybe instead you want to learn more about the garbage collector & memory allocator and mark a capsuled tree as uncollectible? In this way the memory cost would be dynamic and opt-in rather than a high-water mark for the largest allocation somebody changes the capsuleio_amount_bytes const to.

Copy link
Collaborator
@dhalbert dhalbert Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alarm.sleep_memory and microcontroller.nvm both provide persistent storage with a simple bytearray-style of storage. Rather than pass a string, arbitrary bytes is more flexible, though a bit less convenient. Anything can be converted to bytes. The API of those modules would be what to follow. This would be called supervisor.persistent_memory or something like that.

@WarriorOfWire I have seen use cases of needing persistent storage and restarting something every few seconds. For some chips I calculated this would reach the specified flash lifetime after a few weeks. (Useful rule of thumb: a million seconds is about 11 and a half days.)

Rather than reserving a chunk of RAM permanently, the data in question can be copied out of the heap to a safe place when the VM exits, and then put back after the restart. The "safe place" might be on the stack, or just someplace that's untouched while the VM is not running. It might even work to leave it in its old heap location. As soon as the heap is re-created, a new heap object of the right size can be created and the old data copied there.

Copy link
Author
@TG-Techie TG-Techie Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API

alarm.sleep_memory and microcontroller.nvm both provide persistent storage with a simple bytearray-style of storage

@dhalbert That sounds like a much better interface, especially since that mirrors the literal, physical underlying hardware. I really like your suggestion of putting it with the supervisor namespace as the "time capsule"* should share the same lifetime.


@WarriorOfWire: Agreed 1k is not a reasonable amount of memory for non-full builds of cp. The ..._CAPSULE_AMOUNT_BYTES should definitely change for the size of the build (maybe m0 = 128, m4/nrf=512?, imx=even more?).
It's also not uncommon for 'niceties' like gamepad, json, or ulab to be excluded from small builds, maybe only full builds of cp should include this?

Implementation

Maybe instead you want to learn more about the garbage collector & memory allocator and mark a capsuled tree as uncollectible?

Opt-in does sound a lot better than consistent memory hit and at the same time I'd be wary to mark an arbitrary tree of python objects as 'do not collect'? Though the supervisor.time_capsule* interface would not have that option.

Rather than reserving a chunk of RAM permanently, the data in question can be copied out of the heap to a safe place when the VM exits, and then put back after the restart.

Could you elaborate, please? What is the advantage of copying in and out of the safe place vs storing directly to the safe place?

separate thought

A side note on naming, 'persistent' can be another name for 'non-volatile' in the memory hierarchy. IIRC previous conversations have discussed calling it "scratchpad" but that may not be clear.

An additional difference is on disk and .nvm will persist between resets, not just reloads.
The guarantee that the "time capsule"* will always init to all zeros on reset. I think there is some safety there, though that was not a motivating reason to implement this.

* whatever the final name becomes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate, please? What is the advantage of copying in and out of the safe place vs storing directly to the safe place?

It doesn't have to be pre-allocated and use up precious space in RAM if not used.

I have a somewhat better API idea:

Don't provide a fixed-size area but to instead just do something like supervisor.persisted_bytes = some bytearray or bytes. So it could be as large or small as you want. When the VM exits, the bytes are either left where they are (as described above) or copied to someplace temporary (like the stack). When the VM restarts, a new object with the bytes is created, you can fetch it via supervisor.persisted_bytes. If you want to throw away what you persisted, you can do supervisor.persisted_bytes = None.

Copy link
Author
@TG-Techie TG-Techie Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @Neradoc pointed alarm.sleep_memory has the same functionality.
The main differences appear to be:

  1. It is port dependent and seems to only be supported on esp32s2 (stably that is)
  2. It is grouped with the alarm module meaning implementing it is paired to the alarm module
  3. It is fixed length where this could be dynamic length

Possible Solution

merge alarm.sleep_memory and capsule as a supervisor.saved_memory* singleton with a max length that is included in only full builds of python.

For boards that do not have specialized features, a common implementation could do the on-stack (or safe place or pystack) swap. Here having a max length means you can't request to save something so large it would overflow the stack.

For ports that have specialized peripherals/memories, it is implemented on top of those.

The max length would vary per port.

*still working title

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Why limit the max length?
  • Why take ram away from devices whether they use the feature or not?
  • If you prefer, the previous 2 can be summed up as “why does this use static allocation?”
  • supervisor.persisted_bytes was proposed as a name to use. I +1 that name.

@WarriorOfWire: Agreed 1k is not a reasonable amount of memory for non-full builds of cp.

This is not what I attempted to communicate - I intended to represent that statically allocating a buffer that will be rarely used and is not even in a hot path is hostile to users’ memory. It is hostile whether it is a full build or not. 128 bits is 10 vectorio circles with xy locations. It’s not insignificant on any build and this memory cost ought only affect users of the api.

Choosing different buffer sizes per board also makes it more painful to use - and to Dan’s point which I share, it’s needless right?

Copy link
Author
@TG-Techie TG-Techie Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for re-framing your point and elaborating on Dan's. I now see how I miss interpreted both of your statements and guidance.
I hear you regarding statically allocating and how having the persisted data be any size tailors any cost involved not to the board or build but rather to the specific use case/call (?). That went right over my head for a bit: thank you for explaining.

I have a few questions to make sure I head in the right direction:

  • Would it make sense for the message saving (whether by marking uncollectable or copying) would go somewhere in or between stop_mp, free_memory, or supervisor_move_memory? link
  • If so, where would you suggest putting the message restore procedure call? I'd hazard a guess it would go somewhere in or between stack_resize, allocate_remaining_memory, and start_mp? link

Copy link
Author
@TG-Techie TG-Techie Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the right direction" being towards supervisor. persisted_bytes as Dan has suggested above.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would store .persistent_bytes in the supervisor module dictionary, which we would make mutable. We would treat it similarly to the .wake_alarm attribute in alarm, whose module dictionary is also mutable. gc_collect_ptr() is called specially on the .wake_alarm object, for instance.

{ MP_ROM_QSTR(MP_QSTR_wake_alarm), mp_const_none },

I do not remember if anything is done with the heap when the VM is stopped. That would have to be researched. If the heap is left alone, we don't need to copy the bytes until we start up the VM again, before we reinitialize the heap. The bytes could be copied onto the stack, the heap would be initialized, and then a bytes object would be allocated for the saved bytes and they would be copied in. Since the original object might be anywhere in heap storage, I don't think we can rely on it not being smashed up in some way when the heap is initialized (but I don't know the details of heap initialization either).

This is somewhat complicated and will require tracing through some internals stuff to make sure. I cannot say for sure "put some new code here and there" without re-understanding the detail VM and heap shutdown and startup code.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think others are better informed about the lifecycle of the Circuitpython init/teardown than me, though I do need an excuse to use this Segger. I’d love to see what Dan has to say there ❤️
Also to be clear, I don’t particularly care whether it’s a copy or marked objects. My care is that users should only “pay for what they use” as far as ram goes and just about any dynamic allocation strategy will accomplish that.


STATIC bool capsuleio_bury_string(const byte *data, size_t length) {
// the only fail mode is the string is too long
// make sure the data will fit
if (length > CIRCUITPY_CAPSULEIO_AMOUNT_BYTES) {
return false;
} else {
// set the type of the data stored
capsuleio_capsule.kind = CAPSULEIO_STRING;
// copy the string data in
memcpy(&capsuleio_capsule.data, data, length);
// write the null byte
capsuleio_capsule.data[length] = 0;
return true;
}
}

STATIC void capsuleio_bury_none(void) {
capsuleio_capsule.kind = CAPSULEIO_NONE;
memset(&capsuleio_capsule.data, 0, CIRCUITPY_CAPSULEIO_AMOUNT_BYTES);
return;
}

mp_obj_t capsuleio_unearth_new_obj(void) {
switch (capsuleio_capsule.kind) {
case CAPSULEIO_NONE:
return mp_const_none;
case CAPSULEIO_STRING:
return mp_obj_new_str_copy(
&mp_type_str,
(const byte *)&capsuleio_capsule.data,
strlen((const char *)&capsuleio_capsule.data));
default:
// this should never be reached, but just in case
return mp_const_none;
}
}

capsule_result_t capsuleio_bury_obj(mp_obj_t obj) {
if (obj == mp_const_none) {
capsuleio_bury_none();
return CAPSULEIO_OK;
} else if (MP_OBJ_IS_STR(obj)) {
GET_STR_DATA_LEN(obj, data, length); // defines locals data and length
bool bury_worked = capsuleio_bury_string(data, length);
if (!bury_worked) {
return CAPSULEIO_STRING_TO_LONG;
} else {
return CAPSULEIO_OK;
}
} else {
return CAPSULEIO_TYPE_CANNOT_BE_BURIED;
}
}
Loading
0