1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

feat(fs): file writes update the file cache (#6186)

Co-authored-by: kevinkang-Globe <139213362+kevinkang-Globe@users.noreply.github.com>
This commit is contained in:
Liam 2024-05-13 13:09:22 -04:00 committed by GitHub
parent e3f70a2987
commit 4e85b0faa5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 378 additions and 149 deletions

View File

@ -42,7 +42,8 @@ extensions = [
'sphinx_sitemap',
'lv_example',
'sphinx_rtd_dark_mode',
'link_roles'
'link_roles',
'sphinxcontrib.mermaid'
]
default_dark_mode = False

View File

@ -34,7 +34,7 @@ The work directory can be set with ``LV_FS_..._PATH``. E.g.
``"/home/joe/projects/"`` The actual file/directory paths will be
appended to it.
Cached reading is also supported if ``LV_FS_..._CACHE_SIZE`` is set to
:ref:`Cached reading <overview_file_system_cache>` is also supported if ``LV_FS_..._CACHE_SIZE`` is set to
not ``0`` value. :cpp:func:`lv_fs_read` caches this size of data to lower the
number of actual reads from the storage.

View File

@ -155,6 +155,95 @@ To use files in image widgets the following callbacks are required:
- seek
- tell
.. _overview_file_system_cache:
Optional file buffering/caching
*******************************
Files will buffer their reads if the corresponding ``LV_FS_*_CACHE_SIZE``
config option is set to a value greater than zero. Each open file will
buffer up to that many bytes to reduce the number of FS driver calls.
Generally speaking, file buffering can be optimized for different kinds
of access patterns. The one implemented here is optimal for reading large
files in chunks, which is what the image decoder does.
It has the potential to call the driver's ``read`` fewer
times than ``lv_fs_read`` is called. In the best case where the cache size is
\>= the size of the file, ``read`` will only be called once. This strategy is good
for linear reading of large files but less helpful for short random reads across a file bigger than the buffer
since data will be buffered that will be discarded after the next seek and read.
The cache should be sufficiently large or disabled in that case. Another case where the cache should be disabled
is if the file contents are expected to change by an external factor like with special OS files.
The implementation is documented below. Note that the FS functions make calls
to other driver FS functions when the cache is enabled. i.e., ``lv_fs_read`` may call the driver's ``seek``
so the driver needs to implement more callbacks when the cache is enabled.
``lv_fs_read`` :sub:`(behavior when the cache is enabled)`
-------------------------------------------------
.. mermaid::
:zoom:
%%{init: {'theme':'neutral'}}%%
flowchart LR
A["call lv_fs_read and
the cache is enabled"] --> B{{"is there cached data
at the file position?"}}
B -->|yes| C{{"does the cache have
all required bytes available?"}}
C -->|yes| D["copy all required bytes from
the cache to the destination
buffer"]
C -->|no| F["copy the available
required bytes
until the end of the cache
into the destination buffer"]
--> G["seek the real file to the end
of what the cache had available"]
--> H{{"is the number of remaining bytes
larger than the size of the whole cache?"}}
H -->|yes| I["read the remaining bytes
from the real file to the
destination buffer"]
H -->|no| J["eagerly read the real file
to fill the whole cache
or as many bytes as the
read call can"]
--> O["copy the required bytes
to the destination buffer"]
B -->|no| K["seek the real file to
the file position"]
--> L{{"is the number of required
bytes greater than the
size of the entire cache?"}}
L -->|yes| M["read the real file to
the destination buffer"]
L -->|no| N["eagerly read the real file
to fill the whole cache
or as many bytes as the
read call can"]
--> P["copy the required bytes
to the destination buffer"]
``lv_fs_write`` :sub:`(behavior when the cache is enabled)`
--------------------------------------------------
The part of the cache that coincides with the written content
will be updated to reflect the written content.
``lv_fs_seek`` :sub:`(behavior when the cache is enabled)`
-------------------------------------------------
The driver's ``seek`` will not actually be called unless the ``whence``
is ``LV_FS_SEEK_END``, in which case ``seek`` and ``tell`` will be called
to determine where the end of the file is.
``lv_fs_tell`` :sub:`(behavior when the cache is enabled)`
-------------------------------------------------
The driver's ``tell`` will not actually be called.
.. _overview_file_system_api:
API

View File

@ -10,5 +10,6 @@ sphinxcontrib-htmlhelp
sphinxcontrib-jsmath
sphinxcontrib-qthelp
sphinxcontrib-serializinghtml
sphinxcontrib-mermaid
sphinx-rtd-dark-mode
typing-extensions

View File

@ -27,6 +27,9 @@
* STATIC PROTOTYPES
**********************/
static const char * lv_fs_get_real_path(const char * path);
static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br);
static lv_fs_res_t lv_fs_write_cached(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw);
static lv_fs_res_t lv_fs_seek_cached(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence);
/**********************
* STATIC VARIABLES
@ -171,100 +174,17 @@ lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p)
return res;
}
static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t btr, uint32_t * br)
{
LV_PROFILER_BEGIN;
lv_fs_res_t res = LV_FS_RES_OK;
uint32_t file_position = file_p->cache->file_position;
uint32_t start = file_p->cache->start;
uint32_t end = file_p->cache->end;
char * buffer = file_p->cache->buffer;
uint32_t buffer_size = file_p->drv->cache_size;
if(start <= file_position && file_position <= end) {
/* Data can be read from cache buffer */
uint32_t buffer_remaining_length = (uint32_t)end - file_position + 1;
uint32_t buffer_offset = (end - start) - buffer_remaining_length + 1;
/* Do not allow reading beyond the actual memory block for memory-mapped files */
if(file_p->drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
if(btr > buffer_remaining_length)
btr = buffer_remaining_length - 1;
}
if(btr <= buffer_remaining_length) {
/*Data is in cache buffer, and buffer end not reached, no need to read from FS*/
lv_memcpy(buf, buffer + buffer_offset, btr);
*br = btr;
}
else {
/*First part of data is in cache buffer, but we need to read rest of data from FS*/
lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length);
file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->end + 1,
LV_FS_SEEK_SET);
uint32_t bytes_read_to_buffer = 0;
if(btr - buffer_remaining_length > buffer_size) {
/*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length),
btr - buffer_remaining_length, &bytes_read_to_buffer);
}
else {
/*If remaining data chunk is smaller than buffer size, then read into cache buffer*/
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
file_p->cache->start = file_p->cache->end + 1;
file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer);
lv_memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining);
}
*br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr);
}
}
else {
file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position,
LV_FS_SEEK_SET);
/*Data is not in cache buffer*/
if(btr > buffer_size) {
/*If bigger data is requested, then do not use cache, instead read it directly*/
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br);
}
else {
/*If small data is requested, then read from FS into cache buffer*/
if(buffer == NULL) {
file_p->cache->buffer = lv_malloc(buffer_size);
LV_ASSERT_MALLOC(file_p->cache->buffer);
buffer = file_p->cache->buffer;
}
uint32_t bytes_read_to_buffer = 0;
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
file_p->cache->start = file_position;
file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
*br = LV_MIN(btr, bytes_read_to_buffer);
lv_memcpy(buf, buffer, *br);
}
}
if(res == LV_FS_RES_OK) {
file_p->cache->file_position += *br;
}
LV_PROFILER_END;
return res;
}
lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
{
if(br != NULL) *br = 0;
if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM;
if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
if(file_p->drv->cache_size) {
if(file_p->drv->read_cb == NULL || file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
}
else {
if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
}
LV_PROFILER_BEGIN;
@ -272,7 +192,7 @@ lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t
lv_fs_res_t res;
if(file_p->drv->cache_size) {
res = lv_fs_read_cached(file_p, (char *)buf, btr, &br_tmp);
res = lv_fs_read_cached(file_p, buf, btr, &br_tmp);
}
else {
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp);
@ -293,30 +213,25 @@ lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, u
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->write_cb == NULL) {
return LV_FS_RES_NOT_IMP;
if(file_p->drv->cache_size) {
if(file_p->drv->write_cb == NULL || file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
}
else {
if(file_p->drv->write_cb == NULL) return LV_FS_RES_NOT_IMP;
}
LV_PROFILER_BEGIN;
lv_fs_res_t res = LV_FS_RES_OK;
/*Need to do FS seek before writing data to FS*/
if(file_p->drv->cache_size) {
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) {
LV_PROFILER_END;
return res;
}
}
lv_fs_res_t res;
uint32_t bw_tmp = 0;
res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
if(file_p->drv->cache_size) {
res = lv_fs_write_cached(file_p, buf, btw, &bw_tmp);
}
else {
res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
}
if(bw != NULL) *bw = bw_tmp;
if(file_p->drv->cache_size && res == LV_FS_RES_OK)
file_p->cache->file_position += bw_tmp;
LV_PROFILER_END;
return res;
@ -328,49 +243,18 @@ lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whenc
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->seek_cb == NULL) {
return LV_FS_RES_NOT_IMP;
if(file_p->drv->cache_size) {
if(file_p->drv->seek_cb == NULL || file_p->drv->tell_cb == NULL) return LV_FS_RES_NOT_IMP;
}
else {
if(file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
}
LV_PROFILER_BEGIN;
lv_fs_res_t res = LV_FS_RES_OK;
lv_fs_res_t res;
if(file_p->drv->cache_size) {
switch(whence) {
case LV_FS_SEEK_SET: {
file_p->cache->file_position = pos;
/*FS seek if new position is outside cache buffer*/
if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
}
break;
}
case LV_FS_SEEK_CUR: {
file_p->cache->file_position += pos;
/*FS seek if new position is outside cache buffer*/
if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
}
break;
}
case LV_FS_SEEK_END: {
/*Because we don't know the file size, we do a little trick: do a FS seek, then get the new file position from FS*/
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
if(res == LV_FS_RES_OK) {
uint32_t tmp_position;
res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position);
if(res == LV_FS_RES_OK) {
file_p->cache->file_position = tmp_position;
}
}
break;
}
}
res = lv_fs_seek_cached(file_p, pos, whence);
}
else {
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
@ -388,7 +272,7 @@ lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos)
return LV_FS_RES_INV_PARAM;
}
if(file_p->drv->tell_cb == NULL) {
if(file_p->drv->cache_size == 0 && file_p->drv->tell_cb == NULL) {
*pos = 0;
return LV_FS_RES_NOT_IMP;
}
@ -621,3 +505,162 @@ static const char * lv_fs_get_real_path(const char * path)
return path;
}
static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
{
lv_fs_res_t res = LV_FS_RES_OK;
uint32_t file_position = file_p->cache->file_position;
uint32_t start = file_p->cache->start;
uint32_t end = file_p->cache->end;
char * buffer = file_p->cache->buffer;
uint32_t buffer_size = file_p->drv->cache_size;
if(start <= file_position && file_position <= end) {
/* Data can be read from cache buffer */
uint32_t buffer_remaining_length = (uint32_t)end - file_position + 1;
uint32_t buffer_offset = (end - start) - buffer_remaining_length + 1;
/* Do not allow reading beyond the actual memory block for memory-mapped files */
if(file_p->drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
if(btr > buffer_remaining_length)
btr = buffer_remaining_length - 1;
}
if(btr <= buffer_remaining_length) {
/*Data is in cache buffer, and buffer end not reached, no need to read from FS*/
lv_memcpy(buf, buffer + buffer_offset, btr);
*br = btr;
}
else {
/*First part of data is in cache buffer, but we need to read rest of data from FS*/
lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length);
file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->end + 1,
LV_FS_SEEK_SET);
uint32_t bytes_read_to_buffer = 0;
if(btr - buffer_remaining_length > buffer_size) {
/*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (char *)buf + buffer_remaining_length,
btr - buffer_remaining_length, &bytes_read_to_buffer);
}
else {
/*If remaining data chunk is smaller than buffer size, then read into cache buffer*/
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buffer, buffer_size, &bytes_read_to_buffer);
file_p->cache->start = file_p->cache->end + 1;
file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer);
lv_memcpy((char *)buf + buffer_remaining_length, buffer, data_chunk_remaining);
}
*br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr);
}
}
else {
file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position,
LV_FS_SEEK_SET);
/*Data is not in cache buffer*/
if(btr > buffer_size) {
/*If bigger data is requested, then do not use cache, instead read it directly*/
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br);
}
else {
/*If small data is requested, then read from FS into cache buffer*/
if(buffer == NULL) {
file_p->cache->buffer = lv_malloc(buffer_size);
LV_ASSERT_MALLOC(file_p->cache->buffer);
buffer = file_p->cache->buffer;
}
uint32_t bytes_read_to_buffer = 0;
res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
file_p->cache->start = file_position;
file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
*br = LV_MIN(btr, bytes_read_to_buffer);
lv_memcpy(buf, buffer, *br);
}
}
if(res == LV_FS_RES_OK) {
file_p->cache->file_position += *br;
}
return res;
}
static lv_fs_res_t lv_fs_write_cached(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
{
lv_fs_res_t res = LV_FS_RES_OK;
/*Need to do FS seek before writing data to FS*/
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
if(res != LV_FS_RES_OK) return res;
res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, bw);
if(res != LV_FS_RES_OK) return res;
if(file_p->cache->end >= file_p->cache->start) {
uint32_t start_position = file_p->cache->file_position;
uint32_t end_position = file_p->cache->file_position + *bw - 1;
char * cache_buffer = file_p->cache->buffer;
const char * write_buffer = buf;
if(start_position <= file_p->cache->start && end_position >= file_p->cache->end) {
lv_memcpy(cache_buffer,
write_buffer + (file_p->cache->start - start_position),
file_p->cache->end + 1 - file_p->cache->start);
}
else if(start_position >= file_p->cache->start && end_position <= file_p->cache->end) {
lv_memcpy(cache_buffer + (start_position - file_p->cache->start),
write_buffer,
end_position + 1 - start_position);
}
else if(end_position >= file_p->cache->start && end_position <= file_p->cache->end) {
lv_memcpy(cache_buffer,
write_buffer + (file_p->cache->start - start_position),
end_position + 1 - file_p->cache->start);
}
else if(start_position >= file_p->cache->start && start_position <= file_p->cache->end) {
lv_memcpy(cache_buffer + (start_position - file_p->cache->start),
write_buffer,
file_p->cache->end + 1 - start_position);
}
}
file_p->cache->file_position += *bw;
return res;
}
static lv_fs_res_t lv_fs_seek_cached(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
{
lv_fs_res_t res = LV_FS_RES_OK;
switch(whence) {
case LV_FS_SEEK_SET: {
file_p->cache->file_position = pos;
break;
}
case LV_FS_SEEK_CUR: {
file_p->cache->file_position += pos;
break;
}
case LV_FS_SEEK_END: {
/*Because we don't know the file size, we do a little trick: do a FS seek, then get the new file position from FS*/
res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
if(res == LV_FS_RES_OK) {
uint32_t tmp_position;
res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position);
if(res == LV_FS_RES_OK) {
file_p->cache->file_position = tmp_position;
}
}
break;
}
}
return res;
}

View File

@ -178,4 +178,99 @@ static void read_random_drv(char drv_letter, uint32_t cache_size)
drv->cache_size = original_cache_size;
}
void test_write_read_random(void)
{
lv_fs_drv_t * drv = lv_fs_get_drv('A');
uint32_t original_cache_size = drv->cache_size;
drv->cache_size = 7;
/* create the file and reopen for read+write. stdio "rb+" mode requires the file to exist */
lv_fs_res_t res;
lv_fs_file_t f;
res = lv_fs_open(&f, "A:fs_write_read_random.bin", LV_FS_MODE_WR);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
res = lv_fs_close(&f);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
res = lv_fs_open(&f, "A:fs_write_read_random.bin", LV_FS_MODE_WR | LV_FS_MODE_RD);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
uint8_t buf1000[1000];
for(uint32_t i = 0; i < 1000; i++) buf1000[i] = i;
res = lv_fs_write(&f, buf1000, 1000, NULL);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
/**
* {{{action, amount}, ... x20 actions per combo}, ... x100 combos}
* actions are read (0), write (1), seek (2)
* read and write amount ranges from 1 to 20. seek amount ranges from -20 to 20.
* seeks occur as frequently as reads+writes combined.
*/
// *INDENT-OFF*
static const int8_t actions[100][20][2] = {
{{2, -20}, {2, 17}, {0, 20}, {2, 19}, {1, 18}, {1, 4}, {1, 13}, {2, -7}, {1, 6}, {1, 19}, {2, 3}, {1, 12}, {2, 12}, {2, -2}, {2, -20}, {2, -17}, {1, 15}, {2, -17}, {0, 18}, {2, 14}}, {{2, 19}, {2, 17}, {2, -12}, {2, -5}, {2, -1}, {2, -17}, {1, 4}, {2, 19}, {2, 3}, {2, 1}, {2, 10}, {2, 18}, {0, 10}, {2, 8}, {2, 17}, {2, 20}, {0, 5}, {0, 14}, {0, 6}, {1, 8}}, {{0, 1}, {2, -20}, {2, -12}, {1, 9}, {0, 9}, {2, 19}, {1, 8}, {0, 13}, {0, 5}, {2, -7}, {2, -14}, {0, 8}, {2, 13}, {1, 5}, {0, 3}, {0, 16}, {2, 19}, {0, 4}, {1, 6}, {2, -4}}, {{0, 1}, {2, 12}, {1, 7}, {1, 17}, {1, 19}, {0, 8}, {1, 1}, {2, 9}, {2, -14}, {2, 14}, {2, -17}, {2, 11}, {1, 4}, {2, 19}, {2, -18}, {1, 12}, {2, -14}, {0, 9}, {1, 7}, {2, 17}}, {{2, -6}, {0, 13}, {1, 15}, {1, 8}, {2, -1}, {2, 15}, {2, 3}, {2, -16}, {2, -11}, {2, 14}, {2, -19}, {2, 6}, {1, 10}, {1, 6}, {1, 12}, {2, 12}, {0, 10}, {2, 10}, {2, -4}, {2, 5}}, {{2, -6}, {1, 2}, {1, 11}, {1, 19}, {2, 2}, {2, 20}, {2, -19}, {2, -7}, {0, 13}, {2, 4}, {1, 6}, {0, 9}, {0, 6}, {2, -1}, {2, -5}, {2, 14}, {2, 15}, {0, 4}, {0, 6}, {0, 10}}, {{1, 17}, {1, 17}, {0, 15}, {0, 3}, {1, 8}, {2, -9}, {1, 13}, {0, 20}, {0, 3}, {1, 17}, {2, -8}, {2, -1}, {1, 13}, {0, 13}, {0, 8}, {1, 1}, {2, -4}, {2, 13}, {2, 17}, {2, -14}}, {{1, 17}, {1, 13}, {1, 17}, {1, 5}, {1, 3}, {0, 20}, {2, 12}, {0, 13}, {2, 5}, {2, -10}, {2, 10}, {0, 6}, {2, 18}, {0, 17}, {2, 2}, {1, 5}, {2, 16}, {2, -10}, {0, 2}, {2, -9}}, {{2, 7}, {2, -2}, {1, 14}, {0, 15}, {1, 2}, {2, 6}, {2, 11}, {1, 2}, {0, 12}, {2, 9}, {0, 17}, {0, 15}, {2, -19}, {0, 11}, {2, 3}, {2, 15}, {2, 19}, {2, -16}, {1, 6}, {1, 15}}, {{2, 12}, {1, 2}, {2, -4}, {2, -20}, {0, 18}, {2, -16}, {1, 15}, {2, -9}, {2, -7}, {1, 1}, {0, 7}, {0, 18}, {1, 18}, {0, 2}, {2, 17}, {1, 2}, {1, 8}, {1, 18}, {2, -7}, {0, 2}},
{{2, 19}, {2, 17}, {2, -7}, {0, 17}, {0, 18}, {1, 4}, {1, 1}, {0, 3}, {2, 4}, {2, -4}, {1, 20}, {0, 1}, {2, 20}, {0, 6}, {2, 10}, {0, 19}, {2, -1}, {0, 7}, {0, 16}, {1, 13}}, {{2, 10}, {2, 13}, {2, 15}, {2, 3}, {2, 12}, {0, 14}, {2, 6}, {2, 14}, {2, -13}, {2, 18}, {0, 19}, {2, -8}, {2, -9}, {0, 12}, {2, -20}, {2, -6}, {2, 15}, {2, 18}, {1, 18}, {1, 10}}, {{0, 15}, {2, -6}, {2, 17}, {1, 7}, {0, 9}, {0, 11}, {0, 12}, {0, 20}, {2, -1}, {2, -8}, {0, 13}, {2, 19}, {1, 3}, {0, 18}, {1, 13}, {2, 12}, {2, 13}, {1, 5}, {2, -14}, {2, 18}}, {{0, 6}, {1, 4}, {1, 6}, {0, 2}, {2, 8}, {1, 9}, {2, 20}, {0, 9}, {2, -1}, {2, 5}, {0, 18}, {2, 8}, {2, 12}, {2, -14}, {0, 18}, {1, 1}, {2, -15}, {2, 14}, {2, -11}, {2, -2}}, {{1, 4}, {2, 6}, {2, 18}, {1, 19}, {0, 18}, {0, 19}, {0, 14}, {0, 12}, {2, 3}, {0, 19}, {2, -20}, {0, 8}, {2, -15}, {2, -15}, {0, 8}, {1, 17}, {2, 2}, {1, 14}, {0, 11}, {0, 13}}, {{2, -10}, {2, -8}, {1, 18}, {1, 11}, {1, 5}, {0, 2}, {2, -19}, {2, -9}, {1, 5}, {2, -1}, {2, 2}, {1, 5}, {0, 9}, {0, 17}, {2, 2}, {0, 10}, {1, 12}, {1, 10}, {0, 8}, {2, -13}}, {{0, 13}, {1, 5}, {2, -1}, {2, -16}, {2, 2}, {0, 6}, {2, -11}, {0, 5}, {2, 5}, {2, -6}, {2, -6}, {1, 19}, {0, 8}, {2, -16}, {2, 14}, {0, 11}, {0, 14}, {0, 18}, {0, 14}, {2, 1}}, {{2, 3}, {2, -2}, {0, 10}, {1, 1}, {0, 10}, {2, -15}, {2, 19}, {2, 2}, {1, 15}, {1, 10}, {2, -18}, {0, 20}, {0, 10}, {2, 3}, {1, 13}, {0, 8}, {0, 16}, {0, 14}, {2, 13}, {2, -7}}, {{0, 4}, {1, 7}, {0, 2}, {0, 15}, {0, 20}, {1, 9}, {0, 14}, {2, 18}, {0, 1}, {0, 4}, {2, -17}, {1, 10}, {1, 15}, {0, 2}, {2, -6}, {2, 4}, {2, 4}, {2, 17}, {2, -13}, {1, 20}}, {{1, 8}, {0, 20}, {2, 4}, {1, 6}, {2, -19}, {1, 4}, {0, 5}, {1, 8}, {1, 17}, {2, -4}, {2, 14}, {1, 10}, {2, 7}, {1, 18}, {2, 17}, {1, 6}, {0, 20}, {2, 1}, {2, -12}, {2, 18}},
{{2, -7}, {1, 12}, {2, 4}, {2, 14}, {0, 13}, {1, 17}, {2, -2}, {2, -9}, {2, -13}, {1, 4}, {2, 4}, {1, 5}, {0, 15}, {2, -11}, {2, 3}, {2, 1}, {2, -11}, {2, 11}, {2, -11}, {0, 4}}, {{1, 7}, {2, 20}, {0, 6}, {0, 9}, {1, 2}, {0, 11}, {2, 4}, {2, -19}, {0, 17}, {2, -7}, {0, 20}, {0, 10}, {1, 11}, {1, 20}, {1, 6}, {2, 14}, {0, 11}, {0, 19}, {2, 4}, {1, 8}}, {{0, 9}, {2, -3}, {1, 17}, {2, 14}, {2, 3}, {1, 19}, {2, 17}, {0, 18}, {2, 16}, {0, 9}, {2, -15}, {1, 14}, {2, 11}, {2, -7}, {0, 17}, {2, 12}, {2, 6}, {2, 9}, {0, 7}, {1, 3}}, {{0, 1}, {2, 13}, {1, 19}, {0, 19}, {2, 6}, {2, -14}, {2, 8}, {2, -18}, {2, 4}, {2, -7}, {0, 12}, {2, 15}, {1, 3}, {2, -19}, {2, 15}, {2, -2}, {1, 19}, {1, 18}, {1, 19}, {0, 16}}, {{2, 7}, {2, 14}, {2, 14}, {2, -19}, {0, 1}, {2, -5}, {0, 17}, {2, 2}, {2, 15}, {1, 13}, {0, 20}, {0, 12}, {2, 20}, {0, 9}, {2, -8}, {0, 14}, {1, 19}, {2, -15}, {2, 17}, {2, -12}}, {{0, 16}, {2, -7}, {2, -4}, {1, 11}, {1, 17}, {2, -14}, {0, 16}, {2, 6}, {1, 19}, {2, 17}, {2, 10}, {2, -12}, {2, -17}, {1, 4}, {2, -14}, {2, -7}, {2, -17}, {2, -1}, {2, -16}, {1, 8}}, {{0, 5}, {0, 15}, {2, 4}, {2, 20}, {2, 7}, {2, 3}, {2, 17}, {0, 8}, {0, 18}, {2, -18}, {1, 1}, {0, 15}, {2, -4}, {0, 13}, {1, 11}, {0, 4}, {2, 11}, {1, 11}, {1, 10}, {2, -9}}, {{2, -5}, {2, -16}, {1, 2}, {1, 17}, {2, 9}, {2, -2}, {0, 7}, {0, 14}, {2, -10}, {0, 6}, {1, 14}, {1, 15}, {2, 12}, {2, 11}, {2, -9}, {2, -10}, {2, -14}, {2, 9}, {0, 13}, {2, 5}}, {{2, 8}, {2, -16}, {2, 20}, {1, 8}, {1, 2}, {2, -5}, {2, -20}, {1, 2}, {1, 4}, {1, 9}, {0, 19}, {2, -18}, {0, 6}, {0, 13}, {1, 5}, {2, 2}, {0, 4}, {1, 19}, {2, 15}, {1, 12}}, {{2, -8}, {0, 16}, {1, 10}, {2, 5}, {2, -16}, {0, 3}, {2, 5}, {0, 6}, {1, 17}, {2, 1}, {2, 13}, {0, 3}, {0, 6}, {0, 6}, {0, 19}, {1, 13}, {2, 19}, {2, 5}, {1, 16}, {2, 5}},
{{2, -19}, {2, -1}, {0, 19}, {2, -3}, {2, 13}, {2, -12}, {0, 2}, {2, -20}, {2, 15}, {2, -9}, {2, -2}, {2, 13}, {2, -6}, {0, 2}, {1, 6}, {2, -1}, {0, 12}, {0, 20}, {2, -14}, {1, 2}}, {{1, 9}, {2, -14}, {2, -2}, {2, -13}, {0, 3}, {1, 6}, {2, 20}, {2, 11}, {2, 17}, {2, 5}, {0, 5}, {1, 1}, {2, -9}, {1, 8}, {2, -2}, {0, 18}, {2, -8}, {1, 11}, {2, 6}, {1, 7}}, {{2, 17}, {2, 14}, {2, 16}, {1, 20}, {2, -1}, {2, -7}, {1, 3}, {1, 14}, {0, 1}, {0, 18}, {2, -14}, {0, 10}, {0, 18}, {2, 19}, {1, 13}, {2, -16}, {1, 17}, {1, 1}, {2, -13}, {0, 13}}, {{2, -2}, {0, 14}, {0, 12}, {2, -8}, {1, 16}, {0, 18}, {2, 5}, {2, 13}, {1, 14}, {2, -4}, {2, 16}, {2, -16}, {2, 16}, {1, 18}, {2, 15}, {2, 4}, {2, -5}, {1, 7}, {2, -20}, {2, -9}}, {{2, 18}, {2, 2}, {2, 5}, {0, 4}, {2, 17}, {2, -15}, {2, 8}, {2, -11}, {1, 8}, {2, -6}, {2, -13}, {0, 5}, {0, 18}, {0, 15}, {2, -8}, {2, -15}, {1, 1}, {2, 4}, {2, -9}, {2, 14}}, {{2, -1}, {2, 1}, {1, 12}, {0, 9}, {2, -11}, {1, 4}, {1, 11}, {1, 5}, {0, 12}, {2, -7}, {1, 1}, {0, 6}, {0, 15}, {2, -10}, {2, -18}, {2, -12}, {0, 3}, {2, 2}, {2, -17}, {2, 1}}, {{2, -5}, {2, 18}, {1, 2}, {2, 14}, {2, 20}, {0, 13}, {0, 11}, {0, 6}, {2, -7}, {2, 13}, {1, 13}, {2, -1}, {1, 12}, {2, -9}, {2, 3}, {1, 6}, {2, -16}, {0, 6}, {0, 9}, {2, -14}}, {{1, 6}, {2, 7}, {2, -13}, {0, 5}, {1, 4}, {0, 15}, {2, 18}, {2, -4}, {2, 16}, {1, 1}, {2, 17}, {0, 2}, {1, 12}, {0, 17}, {1, 18}, {1, 5}, {1, 8}, {1, 4}, {2, -9}, {1, 9}}, {{0, 20}, {1, 10}, {2, 20}, {2, 13}, {0, 18}, {0, 10}, {1, 3}, {0, 8}, {2, -16}, {2, -16}, {2, -2}, {0, 20}, {2, -19}, {2, -11}, {1, 17}, {1, 18}, {1, 5}, {0, 6}, {2, -9}, {1, 8}}, {{0, 18}, {0, 19}, {2, 16}, {2, -14}, {1, 7}, {1, 9}, {0, 16}, {0, 16}, {1, 17}, {0, 14}, {2, -16}, {1, 6}, {1, 11}, {2, 7}, {1, 19}, {1, 11}, {2, -14}, {0, 7}, {1, 12}, {0, 2}},
{{2, 3}, {2, -15}, {0, 13}, {2, -3}, {0, 16}, {1, 18}, {0, 11}, {0, 16}, {1, 13}, {2, -18}, {1, 14}, {2, 11}, {0, 11}, {2, -14}, {2, -19}, {0, 2}, {2, -7}, {0, 5}, {0, 20}, {2, -7}}, {{2, 9}, {2, 5}, {2, 7}, {0, 15}, {2, 4}, {0, 5}, {2, -17}, {2, 16}, {2, 8}, {0, 11}, {0, 1}, {1, 8}, {2, -5}, {1, 1}, {2, 4}, {2, -4}, {2, -17}, {0, 5}, {1, 10}, {0, 5}}, {{0, 11}, {1, 16}, {1, 18}, {0, 5}, {2, -13}, {0, 5}, {1, 13}, {1, 10}, {1, 11}, {0, 5}, {1, 9}, {1, 4}, {1, 19}, {2, 1}, {1, 19}, {1, 18}, {1, 8}, {2, 2}, {2, -5}, {1, 4}}, {{0, 12}, {1, 16}, {2, -1}, {1, 10}, {1, 10}, {2, -19}, {2, -20}, {1, 17}, {1, 19}, {1, 4}, {2, 12}, {0, 1}, {2, -15}, {2, -1}, {2, -10}, {2, -19}, {0, 18}, {0, 6}, {2, 18}, {0, 18}}, {{1, 10}, {0, 13}, {2, 3}, {0, 20}, {0, 19}, {0, 5}, {0, 9}, {0, 5}, {0, 20}, {0, 9}, {2, 16}, {1, 11}, {0, 12}, {2, -17}, {0, 20}, {0, 14}, {1, 17}, {2, 15}, {1, 7}, {2, -1}}, {{2, -10}, {2, 1}, {0, 2}, {2, -11}, {2, -17}, {2, -10}, {1, 9}, {2, -6}, {2, -19}, {1, 14}, {0, 4}, {1, 6}, {2, -9}, {2, 3}, {1, 2}, {2, 19}, {1, 15}, {0, 7}, {0, 4}, {2, 2}}, {{1, 12}, {2, -8}, {2, 13}, {2, -19}, {0, 17}, {0, 20}, {1, 3}, {1, 15}, {1, 20}, {2, 17}, {0, 12}, {2, -2}, {1, 8}, {2, -12}, {0, 11}, {2, 17}, {1, 14}, {1, 13}, {2, 2}, {2, 3}}, {{0, 13}, {1, 13}, {2, -4}, {1, 17}, {1, 1}, {1, 5}, {0, 10}, {1, 12}, {0, 9}, {2, -16}, {0, 13}, {2, 19}, {2, 9}, {1, 18}, {2, -1}, {2, -14}, {0, 5}, {1, 2}, {0, 20}, {2, -19}}, {{1, 7}, {2, 19}, {2, 11}, {2, -16}, {1, 19}, {2, -14}, {0, 7}, {1, 10}, {2, 8}, {0, 12}, {1, 12}, {2, -11}, {2, 6}, {2, -16}, {2, 15}, {1, 20}, {2, -12}, {1, 7}, {2, -16}, {2, 7}}, {{0, 8}, {2, 19}, {1, 12}, {2, -12}, {2, 4}, {1, 10}, {1, 13}, {2, 2}, {2, -14}, {2, -10}, {2, 18}, {1, 16}, {0, 10}, {2, 19}, {2, -1}, {1, 17}, {2, -1}, {0, 14}, {1, 11}, {0, 18}},
{{2, 2}, {2, -2}, {2, 12}, {0, 7}, {0, 14}, {2, -15}, {2, 13}, {2, -3}, {0, 1}, {2, -2}, {1, 2}, {2, 4}, {2, 4}, {2, 16}, {1, 4}, {2, 1}, {1, 9}, {1, 6}, {1, 12}, {1, 11}}, {{2, -10}, {1, 16}, {1, 12}, {0, 18}, {2, -13}, {0, 19}, {2, 15}, {0, 11}, {0, 2}, {0, 4}, {0, 7}, {0, 10}, {2, -10}, {2, 11}, {0, 13}, {0, 10}, {2, -14}, {2, -18}, {0, 7}, {2, 16}}, {{2, -19}, {2, -5}, {1, 9}, {0, 15}, {2, 4}, {2, -14}, {1, 13}, {2, 10}, {2, -7}, {2, 14}, {2, 8}, {2, 9}, {0, 14}, {2, -4}, {1, 2}, {1, 19}, {0, 18}, {2, 18}, {1, 6}, {2, -6}}, {{2, -15}, {2, 1}, {0, 8}, {1, 5}, {2, -11}, {2, -3}, {0, 19}, {0, 4}, {1, 19}, {1, 2}, {1, 2}, {0, 3}, {0, 16}, {2, -3}, {2, 18}, {1, 20}, {2, -16}, {2, -2}, {2, -17}, {1, 15}}, {{1, 19}, {2, -11}, {1, 13}, {0, 14}, {1, 18}, {2, 11}, {2, 13}, {2, -14}, {1, 5}, {2, -6}, {0, 4}, {2, 19}, {0, 12}, {1, 4}, {2, -6}, {2, 8}, {2, 9}, {2, -10}, {2, -3}, {2, -19}}, {{2, 7}, {2, -9}, {2, 5}, {2, 12}, {2, -20}, {2, -3}, {2, -12}, {2, 5}, {0, 6}, {2, 15}, {1, 8}, {1, 9}, {2, 18}, {2, -14}, {2, 10}, {1, 5}, {1, 2}, {0, 14}, {2, 14}, {0, 19}}, {{2, 12}, {0, 9}, {0, 14}, {1, 4}, {2, 4}, {2, -8}, {2, 4}, {0, 13}, {1, 10}, {2, 18}, {2, 13}, {2, 15}, {1, 18}, {2, 7}, {2, -18}, {2, 18}, {1, 9}, {2, -9}, {0, 16}, {2, -14}}, {{0, 16}, {2, 9}, {2, 7}, {1, 6}, {0, 14}, {2, -18}, {1, 14}, {1, 2}, {0, 10}, {1, 6}, {0, 10}, {2, 2}, {1, 1}, {0, 19}, {1, 6}, {2, -3}, {2, 3}, {2, 12}, {2, 5}, {1, 18}}, {{2, -17}, {0, 17}, {0, 5}, {2, 18}, {2, 8}, {0, 14}, {0, 10}, {1, 20}, {2, 7}, {1, 10}, {1, 14}, {1, 10}, {0, 11}, {1, 15}, {0, 16}, {1, 14}, {0, 3}, {2, 16}, {2, -5}, {2, -4}}, {{1, 15}, {2, -9}, {0, 11}, {0, 16}, {2, 1}, {0, 12}, {0, 7}, {0, 16}, {2, 14}, {0, 3}, {1, 11}, {2, 4}, {1, 6}, {2, -1}, {1, 20}, {1, 13}, {0, 2}, {2, -12}, {0, 9}, {2, 8}},
{{0, 1}, {2, -4}, {0, 19}, {2, -15}, {2, -16}, {2, -10}, {2, 7}, {0, 10}, {2, -6}, {2, -8}, {2, -3}, {2, 18}, {1, 20}, {2, 10}, {1, 18}, {2, -3}, {0, 7}, {2, 3}, {2, 14}, {2, 15}}, {{1, 17}, {0, 7}, {1, 17}, {2, -8}, {2, -12}, {2, -9}, {2, 15}, {0, 10}, {1, 10}, {2, -6}, {2, -20}, {1, 17}, {2, -9}, {1, 15}, {1, 3}, {1, 8}, {0, 1}, {1, 13}, {2, -5}, {0, 14}}, {{2, 8}, {0, 11}, {0, 12}, {2, 5}, {2, -4}, {2, -5}, {2, -2}, {0, 20}, {2, -4}, {1, 1}, {1, 2}, {0, 4}, {2, 14}, {1, 17}, {2, -13}, {1, 13}, {2, 12}, {2, 20}, {2, 14}, {2, -9}}, {{2, -20}, {2, 13}, {1, 20}, {2, 20}, {2, 9}, {0, 16}, {2, 1}, {0, 13}, {2, 17}, {2, 8}, {2, 13}, {2, -3}, {2, -1}, {2, -19}, {2, 1}, {1, 16}, {2, -16}, {0, 6}, {2, 2}, {1, 8}}, {{1, 4}, {1, 9}, {1, 8}, {2, -12}, {0, 4}, {1, 8}, {0, 9}, {2, -2}, {2, -19}, {2, 2}, {2, -1}, {0, 14}, {2, 4}, {2, -17}, {0, 1}, {2, 4}, {2, -20}, {2, 3}, {0, 7}, {1, 4}}, {{0, 6}, {2, -5}, {1, 13}, {2, 15}, {2, 11}, {0, 7}, {1, 17}, {2, 9}, {1, 14}, {2, -7}, {2, 12}, {2, -18}, {0, 19}, {2, -1}, {2, 14}, {2, 5}, {2, -16}, {1, 2}, {2, -2}, {0, 17}}, {{1, 11}, {2, 4}, {2, 7}, {1, 16}, {2, 14}, {0, 14}, {2, 16}, {1, 11}, {1, 10}, {2, -3}, {2, -10}, {1, 7}, {2, 9}, {0, 19}, {2, 1}, {0, 7}, {1, 11}, {0, 9}, {2, -16}, {1, 8}}, {{0, 10}, {0, 4}, {2, -8}, {2, 20}, {2, 16}, {2, 5}, {0, 16}, {1, 6}, {2, 2}, {2, 14}, {2, 11}, {1, 15}, {2, -10}, {2, -5}, {0, 4}, {0, 4}, {2, -13}, {2, 11}, {0, 13}, {0, 11}}, {{0, 6}, {1, 4}, {2, 15}, {2, 17}, {1, 1}, {2, -20}, {1, 18}, {0, 19}, {2, -12}, {2, 1}, {2, -15}, {2, -11}, {1, 19}, {0, 13}, {1, 2}, {0, 17}, {2, 14}, {0, 14}, {2, 12}, {0, 19}}, {{2, -16}, {1, 20}, {2, -14}, {1, 7}, {2, -13}, {2, 19}, {2, 20}, {2, -14}, {1, 17}, {2, 20}, {1, 2}, {2, -19}, {2, -17}, {2, 18}, {2, 6}, {1, 17}, {2, 7}, {0, 8}, {1, 20}, {2, 9}},
{{2, 20}, {2, 17}, {2, 15}, {1, 13}, {0, 7}, {2, -6}, {0, 8}, {2, -17}, {1, 15}, {2, -20}, {0, 17}, {2, -17}, {0, 12}, {1, 14}, {2, -8}, {2, -17}, {0, 12}, {2, -3}, {2, -13}, {0, 7}}, {{0, 3}, {2, 12}, {2, 4}, {0, 13}, {0, 15}, {1, 4}, {2, 7}, {2, -20}, {0, 4}, {0, 6}, {0, 12}, {2, 10}, {2, -13}, {0, 20}, {2, -11}, {0, 16}, {2, -13}, {2, -5}, {1, 14}, {0, 7}}, {{2, 16}, {1, 11}, {2, -1}, {0, 9}, {0, 19}, {2, 9}, {2, 6}, {1, 15}, {2, 2}, {2, 8}, {1, 3}, {2, 20}, {2, -18}, {2, -15}, {2, -20}, {0, 10}, {1, 9}, {2, 5}, {2, -17}, {2, 14}}, {{2, -15}, {2, -10}, {2, -18}, {1, 20}, {1, 4}, {0, 18}, {1, 10}, {2, 19}, {2, -9}, {2, 20}, {0, 1}, {0, 11}, {2, -18}, {2, 5}, {1, 15}, {0, 10}, {2, -14}, {2, 6}, {0, 17}, {0, 3}}, {{1, 18}, {1, 5}, {0, 11}, {2, 11}, {0, 13}, {1, 5}, {0, 19}, {2, 20}, {1, 10}, {2, 19}, {0, 13}, {0, 7}, {2, 1}, {2, 14}, {1, 19}, {0, 9}, {1, 17}, {2, 18}, {0, 11}, {1, 9}}, {{2, -17}, {2, 10}, {2, -19}, {2, -18}, {2, 6}, {0, 9}, {0, 20}, {1, 7}, {2, -14}, {2, -11}, {0, 14}, {1, 1}, {0, 13}, {0, 2}, {2, 3}, {0, 2}, {2, 11}, {2, 10}, {2, 12}, {2, -4}}, {{2, 13}, {1, 14}, {2, 7}, {1, 12}, {2, -9}, {2, -4}, {2, -8}, {2, 13}, {0, 3}, {1, 20}, {2, 2}, {2, 5}, {0, 19}, {0, 10}, {2, 14}, {2, -15}, {1, 5}, {2, 18}, {2, -8}, {2, 6}}, {{1, 5}, {1, 9}, {1, 19}, {2, 19}, {0, 13}, {2, 2}, {2, 12}, {2, 17}, {2, -16}, {0, 12}, {0, 9}, {1, 17}, {0, 13}, {2, 7}, {1, 7}, {2, 14}, {0, 12}, {0, 7}, {2, -16}, {1, 7}}, {{2, 15}, {0, 9}, {1, 3}, {0, 4}, {2, -3}, {0, 12}, {2, 9}, {0, 6}, {1, 16}, {0, 10}, {2, -6}, {1, 20}, {2, 11}, {2, -3}, {2, -6}, {0, 15}, {0, 14}, {0, 11}, {2, -19}, {2, -14}}, {{0, 12}, {2, 8}, {1, 20}, {2, 18}, {2, 3}, {2, 2}, {2, 4}, {2, 7}, {2, -19}, {2, -9}, {2, 2}, {0, 19}, {0, 11}, {2, -9}, {0, 9}, {2, -14}, {1, 13}, {0, 2}, {0, 1}, {2, -19}},
{{2, -4}, {1, 6}, {0, 18}, {2, 4}, {1, 12}, {0, 17}, {2, 10}, {2, 13}, {2, 10}, {1, 10}, {2, -1}, {2, -11}, {2, -15}, {2, -5}, {2, -16}, {2, -12}, {0, 2}, {2, -10}, {2, 15}, {1, 2}}, {{2, -9}, {2, -14}, {1, 4}, {0, 5}, {2, -9}, {0, 7}, {0, 9}, {1, 16}, {1, 10}, {2, -14}, {0, 7}, {2, -18}, {0, 19}, {1, 3}, {1, 7}, {2, -19}, {2, 14}, {0, 9}, {1, 7}, {2, -5}}, {{2, 16}, {0, 13}, {2, 20}, {2, -9}, {0, 20}, {2, 20}, {0, 5}, {0, 6}, {1, 4}, {2, -11}, {0, 14}, {0, 9}, {2, 18}, {0, 14}, {0, 12}, {2, 16}, {2, 8}, {2, -13}, {1, 9}, {1, 3}}, {{2, 1}, {2, 13}, {2, -8}, {0, 9}, {2, -16}, {0, 12}, {0, 12}, {2, 19}, {2, -10}, {2, 1}, {2, -20}, {1, 10}, {0, 18}, {2, -15}, {2, -15}, {1, 15}, {0, 15}, {0, 1}, {2, -5}, {0, 19}}, {{2, 6}, {2, 15}, {2, -20}, {1, 20}, {2, 3}, {2, 7}, {1, 1}, {0, 5}, {0, 4}, {2, 8}, {1, 19}, {2, -3}, {2, 18}, {1, 10}, {0, 11}, {2, -19}, {0, 3}, {2, 5}, {1, 4}, {0, 6}}, {{0, 5}, {2, -17}, {1, 1}, {0, 1}, {2, -9}, {0, 8}, {2, -10}, {2, -4}, {0, 13}, {2, 19}, {0, 6}, {2, 9}, {2, 14}, {2, 11}, {0, 16}, {2, 13}, {1, 1}, {2, 9}, {2, 14}, {0, 18}}, {{0, 10}, {1, 12}, {1, 4}, {2, -12}, {2, 7}, {0, 5}, {1, 15}, {2, 7}, {2, 17}, {1, 18}, {2, 3}, {0, 12}, {1, 8}, {1, 13}, {2, -17}, {1, 20}, {0, 18}, {1, 12}, {1, 16}, {0, 16}}, {{2, -10}, {2, -1}, {2, 10}, {1, 4}, {2, -12}, {1, 19}, {2, 18}, {2, -15}, {2, -5}, {1, 11}, {2, -7}, {0, 20}, {2, -12}, {2, -12}, {1, 11}, {2, 8}, {2, 2}, {2, -2}, {2, -4}, {2, 19}}, {{2, 5}, {1, 15}, {1, 18}, {2, 20}, {1, 4}, {0, 7}, {2, -8}, {2, 1}, {2, -6}, {2, 8}, {1, 1}, {2, 12}, {1, 15}, {0, 16}, {1, 13}, {0, 1}, {0, 19}, {0, 20}, {2, -3}, {0, 12}}, {{1, 18}, {2, -4}, {1, 8}, {2, 19}, {1, 11}, {0, 12}, {0, 20}, {2, 4}, {1, 12}, {0, 17}, {2, 8}, {1, 5}, {2, -13}, {1, 3}, {1, 14}, {1, 4}, {0, 20}, {1, 18}, {0, 7}, {0, 17}},
{{2, 14}, {2, 10}, {2, -12}, {2, -15}, {1, 3}, {2, -17}, {1, 12}, {2, 9}, {2, -9}, {0, 11}, {2, 12}, {0, 16}, {0, 13}, {2, -17}, {2, 13}, {2, 11}, {1, 13}, {2, 16}, {0, 14}, {2, -8}}, {{0, 16}, {2, -10}, {1, 12}, {1, 12}, {2, 19}, {1, 5}, {2, 1}, {2, 19}, {0, 17}, {2, -19}, {2, 20}, {0, 7}, {1, 11}, {2, -3}, {2, -19}, {2, -20}, {2, 15}, {2, -18}, {2, 11}, {2, -19}}, {{2, -11}, {2, 10}, {0, 10}, {1, 15}, {1, 1}, {2, 7}, {2, -9}, {2, -3}, {0, 2}, {0, 20}, {0, 1}, {2, 14}, {2, -16}, {1, 14}, {0, 19}, {0, 11}, {2, 4}, {2, -9}, {2, -4}, {2, -10}}, {{2, 2}, {2, 17}, {1, 7}, {0, 18}, {0, 12}, {2, -15}, {2, 18}, {1, 7}, {1, 3}, {2, 14}, {1, 19}, {1, 6}, {2, -19}, {2, 9}, {0, 11}, {1, 13}, {2, -13}, {0, 19}, {1, 15}, {0, 14}}, {{2, 14}, {1, 3}, {2, 4}, {0, 14}, {1, 8}, {2, 7}, {0, 16}, {1, 11}, {2, 11}, {1, 13}, {1, 6}, {2, -10}, {0, 17}, {1, 18}, {1, 1}, {2, -11}, {1, 9}, {0, 16}, {2, 5}, {2, -6}}, {{0, 11}, {2, -2}, {1, 7}, {2, 8}, {2, 20}, {0, 7}, {0, 14}, {2, -17}, {2, 14}, {2, -14}, {2, -16}, {2, -12}, {2, -19}, {2, 10}, {2, -8}, {1, 1}, {1, 14}, {0, 5}, {0, 14}, {2, 14}}, {{2, 10}, {2, 18}, {1, 15}, {1, 15}, {2, 10}, {2, 14}, {1, 15}, {2, -16}, {2, -4}, {2, -1}, {2, -20}, {2, 2}, {1, 6}, {0, 2}, {2, -17}, {0, 13}, {2, -8}, {2, 18}, {2, 9}, {2, -12}}, {{2, -7}, {1, 16}, {2, 15}, {1, 14}, {1, 15}, {0, 8}, {1, 19}, {2, -13}, {2, 17}, {2, 4}, {2, -17}, {2, -2}, {0, 8}, {1, 11}, {2, 14}, {0, 13}, {2, -12}, {2, -1}, {2, -5}, {0, 6}}, {{0, 2}, {2, -1}, {2, -15}, {1, 9}, {0, 17}, {0, 8}, {2, 19}, {0, 5}, {1, 16}, {2, 3}, {1, 17}, {2, -8}, {2, -1}, {2, -4}, {2, -16}, {0, 4}, {2, -8}, {2, 14}, {2, -20}, {2, -15}}, {{0, 5}, {0, 20}, {1, 13}, {0, 13}, {2, 18}, {1, 19}, {0, 7}, {0, 13}, {2, -7}, {2, 11}, {0, 16}, {1, 6}, {2, -19}, {2, 6}, {1, 18}, {2, 4}, {2, -7}, {2, -8}, {2, 20}, {0, 19}}
};
// *INDENT-ON*
uint8_t buf[20];
uint8_t n = 0;
uint32_t bres = 0;
for(uint32_t i = 0; i < 100; i++) {
/* bring the pos back to the middle of the file */
int32_t pos = 500;
res = lv_fs_seek(&f, pos, LV_FS_SEEK_SET);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
for(uint32_t j = 0; j < 20; j++) {
int8_t action = actions[i][j][0];
int8_t amount = actions[i][j][1];
switch(action) {
case 0: /* read */
res = lv_fs_read(&f, buf, amount, &bres);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
TEST_ASSERT_EQUAL(amount, bres);
TEST_ASSERT(0 == memcmp(buf, buf1000 + pos, amount));
pos += amount;
break;
case 1: /* write */
for(int32_t k = 0; k < amount; k++) {
buf[k] = n;
buf1000[pos + k] = n;
n++;
}
res = lv_fs_write(&f, buf, amount, &bres);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
TEST_ASSERT_EQUAL(amount, bres);
pos += amount;
break;
case 2: /* seek */
pos += amount; /* amount may be negative */
res = lv_fs_seek(&f, pos, LV_FS_SEEK_SET);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
break;
}
}
}
/* test SEEK_END */
uint32_t tell_pos;
res = lv_fs_seek(&f, 0, LV_FS_SEEK_END);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
res = lv_fs_tell(&f, &tell_pos);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
TEST_ASSERT_EQUAL(tell_pos, 1000);
res = lv_fs_close(&f);
TEST_ASSERT_EQUAL(LV_FS_RES_OK, res);
drv->cache_size = original_cache_size;
}
#endif