PaulStoffregen преди 4 години
родител
ревизия
37333a6779
променени са 15 файла, в които са добавени 748 реда и са изтрити 0 реда
  1. +28
    -0
      teensy4/sm_alloc_valid.c
  2. +17
    -0
      teensy4/sm_calloc.c
  3. +39
    -0
      teensy4/sm_free.c
  4. +40
    -0
      teensy4/sm_hash.c
  5. +108
    -0
      teensy4/sm_malloc.c
  6. +46
    -0
      teensy4/sm_malloc_stats.c
  7. +78
    -0
      teensy4/sm_pool.c
  8. +17
    -0
      teensy4/sm_realloc.c
  9. +128
    -0
      teensy4/sm_realloc_i.c
  10. +17
    -0
      teensy4/sm_realloc_move.c
  11. +29
    -0
      teensy4/sm_szalloc.c
  12. +65
    -0
      teensy4/sm_util.c
  13. +19
    -0
      teensy4/sm_zalloc.c
  14. +77
    -0
      teensy4/smalloc.h
  15. +40
    -0
      teensy4/smalloc_i.h

+ 28
- 0
teensy4/sm_alloc_valid.c Целия файл

@@ -0,0 +1,28 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

int sm_alloc_valid_pool(struct smalloc_pool *spool, const void *p)
{
struct smalloc_hdr *shdr;

if (!smalloc_verify_pool(spool)) {
errno = EINVAL;
return 0;
}

if (!p) return 0;

shdr = USER_TO_HEADER(p);
if (smalloc_is_alloc(spool, shdr)) return 1;
return 0;
}

int sm_alloc_valid(const void *p)
{
return sm_alloc_valid_pool(&smalloc_curr_pool, p);
}

+ 17
- 0
teensy4/sm_calloc.c Целия файл

@@ -0,0 +1,17 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

void *sm_calloc_pool(struct smalloc_pool *spool, size_t x, size_t y)
{
return sm_zalloc_pool(spool, x * y);
}

void *sm_calloc(size_t x, size_t y)
{
return sm_calloc_pool(&smalloc_curr_pool, x, y);
}

+ 39
- 0
teensy4/sm_free.c Целия файл

@@ -0,0 +1,39 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

void sm_free_pool(struct smalloc_pool *spool, void *p)
{
struct smalloc_hdr *shdr;
char *s;

if (!smalloc_verify_pool(spool)) {
errno = EINVAL;
return;
}

if (!p) return;

shdr = USER_TO_HEADER(p);
if (smalloc_is_alloc(spool, shdr)) {
if (spool->do_zero) memset(p, 0, shdr->rsz);
s = CHAR_PTR(p);
s += shdr->usz;
memset(s, 0, HEADER_SZ);
if (spool->do_zero) memset(s+HEADER_SZ, 0, shdr->rsz - shdr->usz);
memset(shdr, 0, HEADER_SZ);
return;
}

smalloc_UB(spool, p);
return;
}

void sm_free(void *p)
{
sm_free_pool(&smalloc_curr_pool, p);
}

+ 40
- 0
teensy4/sm_hash.c Целия файл

@@ -0,0 +1,40 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

/* An adopted Jenkins one-at-a-time hash */
#define UIHOP(x, s) do { \
hash += (x >> s) & 0xff;\
hash += hash << 10; \
hash ^= hash >> 6; \
} while (0)
uintptr_t smalloc_uinthash(uintptr_t x)
{
uintptr_t hash = 0;

UIHOP(x, 0);
UIHOP(x, 8);
UIHOP(x, 16);
UIHOP(x, 24);

hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;

return hash;
}
#undef UIHOP

uintptr_t smalloc_mktag(struct smalloc_hdr *shdr)
{
uintptr_t r = smalloc_uinthash(PTR_UINT(shdr));
r += shdr->rsz;
r = smalloc_uinthash(r);
r += shdr->usz;
r = smalloc_uinthash(r);
return r;
}

+ 108
- 0
teensy4/sm_malloc.c Целия файл

@@ -0,0 +1,108 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

void *sm_malloc_pool(struct smalloc_pool *spool, size_t n)
{
struct smalloc_hdr *basehdr, *shdr, *dhdr;
char *s;
int found;
size_t x;

again: if (!smalloc_verify_pool(spool)) {
errno = EINVAL;
return NULL;
}

if (n == 0) n++; /* return a block successfully */
if (n > SIZE_MAX
|| n > (spool->pool_size - HEADER_SZ)) goto oom;

shdr = basehdr = spool->pool;
while (CHAR_PTR(shdr)-CHAR_PTR(basehdr) < spool->pool_size) {
/*
* Already allocated block.
* Skip it by jumping over it.
*/
if (smalloc_is_alloc(spool, shdr)) {
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += shdr->rsz + HEADER_SZ;
shdr = HEADER_PTR(s);
continue;
}
/*
* Free blocks ahead!
* Do a second search over them to find out if they're
* really large enough to fit the new allocation.
*/
else {
dhdr = shdr; found = 0;
while (CHAR_PTR(dhdr)-CHAR_PTR(basehdr) < spool->pool_size) {
/* pre calculate free block size */
x = CHAR_PTR(dhdr)-CHAR_PTR(shdr);
/*
* ugh, found next allocated block.
* skip this candidate then.
*/
if (smalloc_is_alloc(spool, dhdr))
goto allocblock;
/*
* did not see allocated block yet,
* but this free block is of enough size
* - finally, use it.
*/
if (n + HEADER_SZ <= x) {
x -= HEADER_SZ;
found = 1;
goto outfound;
}
dhdr++;
}

outfound: if (found) {
uintptr_t tag;
/* allocate and return this block */
shdr->rsz = x;
shdr->usz = n;
shdr->tag = tag = smalloc_mktag(shdr);
if (spool->do_zero) memset(HEADER_TO_USER(shdr), 0, shdr->rsz);
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += shdr->usz;
for (x = 0;
x < sizeof(struct smalloc_hdr);
x += sizeof(uintptr_t)) {
tag = smalloc_uinthash(tag);
memcpy(s+x, &tag, sizeof(uintptr_t));
}
memset(s+x, 0xff, shdr->rsz - shdr->usz);
return HEADER_TO_USER(shdr);
}

/* continue first search for next free block */
allocblock: shdr = dhdr;
continue;
}

shdr++;
}

oom: if (spool->oomfn) {
x = spool->oomfn(spool, n);
if (x > spool->pool_size) {
spool->pool_size = x;
if (sm_align_pool(spool)) goto again;
}
}

errno = ENOMEM;
return NULL;
}

void *sm_malloc(size_t n)
{
return sm_malloc_pool(&smalloc_curr_pool, n);
}

+ 46
- 0
teensy4/sm_malloc_stats.c Целия файл

@@ -0,0 +1,46 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

int sm_malloc_stats_pool(struct smalloc_pool *spool, size_t *total, size_t *user, size_t *free, int *nr_blocks)
{
struct smalloc_hdr *shdr, *basehdr;
int r = 0;

if (!smalloc_verify_pool(spool)) {
errno = EINVAL;
return -1;
}

if (!total && !user && !free && !nr_blocks) return 0;

if (total) *total = 0;
if (user) *user = 0;
if (free) *free = 0;
if (nr_blocks) *nr_blocks = 0;

shdr = basehdr = spool->pool;
while (CHAR_PTR(shdr)-CHAR_PTR(basehdr) < spool->pool_size) {
if (smalloc_is_alloc(spool, shdr)) {
if (total) *total += HEADER_SZ + shdr->rsz + HEADER_SZ;
if (user) *user += shdr->usz;
if (nr_blocks) *nr_blocks += 1;
r = 1;
}

shdr++;
}

*free = spool->pool_size - *total;

return r;
}

int sm_malloc_stats(size_t *total, size_t *user, size_t *free, int *nr_blocks)
{
return sm_malloc_stats_pool(&smalloc_curr_pool, total, user, free, nr_blocks);
}

+ 78
- 0
teensy4/sm_pool.c Целия файл

@@ -0,0 +1,78 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

struct smalloc_pool smalloc_curr_pool;

int smalloc_verify_pool(struct smalloc_pool *spool)
{
if (!spool->pool || !spool->pool_size) return 0;
if (spool->pool_size % HEADER_SZ) return 0;
return 1;
}

int sm_align_pool(struct smalloc_pool *spool)
{
size_t x;

if (smalloc_verify_pool(spool)) return 1;

x = spool->pool_size % HEADER_SZ;
if (x) spool->pool_size -= x;
if (spool->pool_size <= MIN_POOL_SZ) {
errno = ENOSPC;
return 0;
}

return 1;
}

int sm_set_pool(struct smalloc_pool *spool, void *new_pool, size_t new_pool_size, int do_zero, smalloc_oom_handler oom_handler)
{
if (!spool) {
errno = EINVAL;
return 0;
}

if (!new_pool || !new_pool_size) {
if (smalloc_verify_pool(spool)) {
if (spool->do_zero) memset(spool->pool, 0, spool->pool_size);
memset(spool, 0, sizeof(struct smalloc_pool));
return 1;
}

errno = EINVAL;
return 0;
}

spool->pool = new_pool;
spool->pool_size = new_pool_size;
spool->oomfn = oom_handler;
if (!sm_align_pool(spool)) return 0;

if (do_zero) {
spool->do_zero = do_zero;
memset(spool->pool, 0, spool->pool_size);
}

return 1;
}

int sm_set_default_pool(void *new_pool, size_t new_pool_size, int do_zero, smalloc_oom_handler oom_handler)
{
return sm_set_pool(&smalloc_curr_pool, new_pool, new_pool_size, do_zero, oom_handler);
}

int sm_release_pool(struct smalloc_pool *spool)
{
return sm_set_pool(spool, NULL, 0, 0, NULL);
}

int sm_release_default_pool(void)
{
return sm_release_pool(&smalloc_curr_pool);
}

+ 17
- 0
teensy4/sm_realloc.c Целия файл

@@ -0,0 +1,17 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

void *sm_realloc_pool(struct smalloc_pool *spool, void *p, size_t n)
{
return sm_realloc_pool_i(spool, p, n, 0);
}

void *sm_realloc(void *p, size_t n)
{
return sm_realloc_pool_i(&smalloc_curr_pool, p, n, 0);
}

+ 128
- 0
teensy4/sm_realloc_i.c Целия файл

@@ -0,0 +1,128 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

/*
* Please do NOT use this function directly or rely on it's presence.
* It may go away in future SMalloc versions, or it's calling
* signature may change. It is internal function, hence "_i" suffix.
*/
void *sm_realloc_pool_i(struct smalloc_pool *spool, void *p, size_t n, int nomove)
{
struct smalloc_hdr *basehdr, *shdr, *dhdr;
void *r;
char *s;
int found;
size_t rsz, usz, x;
uintptr_t tag;

if (!smalloc_verify_pool(spool)) {
errno = EINVAL;
return NULL;
}

if (!p) return sm_malloc_pool(spool, n);
if (!n && p) {
sm_free_pool(spool, p);
return NULL;
}

/* determine user size */
shdr = USER_TO_HEADER(p);
if (!smalloc_is_alloc(spool, shdr)) smalloc_UB(spool, p);
usz = shdr->usz;
rsz = shdr->rsz;

/* newsize is lesser than allocated - truncate */
if (n <= usz) {
if (spool->do_zero) memset(p + n, 0, shdr->rsz - n);
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += usz;
memset(s, 0, HEADER_SZ);
if (spool->do_zero) memset(s+HEADER_SZ, 0, rsz - usz);
shdr->rsz = (n%HEADER_SZ)?(((n/HEADER_SZ)+1)*HEADER_SZ):n;
shdr->usz = n;
shdr->tag = tag = smalloc_mktag(shdr);
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += shdr->usz;
for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) {
tag = smalloc_uinthash(tag);
memcpy(s+x, &tag, sizeof(uintptr_t));
}
memset(s+x, 0xff, shdr->rsz - shdr->usz);
return p;
}

/* newsize is bigger than allocated, but there is free room - modify */
if (n > usz && n <= rsz) {
if (spool->do_zero) {
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += usz;
memset(s, 0, HEADER_SZ);
}
shdr->usz = n;
shdr->tag = tag = smalloc_mktag(shdr);
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += shdr->usz;
for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) {
tag = smalloc_uinthash(tag);
memcpy(s+x, &tag, sizeof(uintptr_t));
}
memset(s+x, 0xff, shdr->rsz - shdr->usz);
return p;
}

/* newsize is bigger, larger than rsz but there are free blocks beyond - extend */
basehdr = spool->pool; dhdr = shdr+(rsz/HEADER_SZ); found = 0;
while (CHAR_PTR(dhdr)-CHAR_PTR(basehdr) < spool->pool_size) {
x = CHAR_PTR(dhdr)-CHAR_PTR(shdr);
if (smalloc_is_alloc(spool, dhdr))
goto allocblock;
if (n + HEADER_SZ <= x) {
x -= HEADER_SZ;
found = 1;
goto outfound;
}
dhdr++;
}

outfound:
/* write new numbers of same allocation */
if (found) {
if (spool->do_zero) {
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += usz;
memset(s, 0, HEADER_SZ);
memset(s+HEADER_SZ, 0, rsz - usz);
}
shdr->rsz = x;
shdr->usz = n;
shdr->tag = tag = smalloc_mktag(shdr);
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += shdr->usz;
for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) {
tag = smalloc_uinthash(tag);
memcpy(s+x, &tag, sizeof(uintptr_t));
}
memset(s+x, 0xff, shdr->rsz - shdr->usz);
return p;
}

allocblock:
/* newsize is bigger than allocated and no free space - move */
if (nomove) {
/* fail if user asked */
errno = ERANGE;
return NULL;
}
r = sm_malloc_pool(spool, n);
if (!r) return NULL;
memcpy(r, p, usz);
sm_free_pool(spool, p);

return r;
}

+ 17
- 0
teensy4/sm_realloc_move.c Целия файл

@@ -0,0 +1,17 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

void *sm_realloc_move_pool(struct smalloc_pool *spool, void *p, size_t n)
{
return sm_realloc_pool_i(spool, p, n, 1);
}

void *sm_realloc_move(void *p, size_t n)
{
return sm_realloc_pool_i(&smalloc_curr_pool, p, n, 1);
}

+ 29
- 0
teensy4/sm_szalloc.c Целия файл

@@ -0,0 +1,29 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

size_t sm_szalloc_pool(struct smalloc_pool *spool, const void *p)
{
struct smalloc_hdr *shdr;

if (!smalloc_verify_pool(spool)) {
errno = EINVAL;
return ((size_t)-1);
}

if (!p) return 0;

shdr = USER_TO_HEADER(p);
if (smalloc_is_alloc(spool, shdr)) return shdr->usz;
smalloc_UB(spool, p);
return 0;
}

size_t sm_szalloc(const void *p)
{
return sm_szalloc_pool(&smalloc_curr_pool, p);
}

+ 65
- 0
teensy4/sm_util.c Целия файл

@@ -0,0 +1,65 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

static int smalloc_check_bounds(struct smalloc_pool *spool, struct smalloc_hdr *shdr)
{
if (!spool) return 0;
if (CHAR_PTR(shdr) >= CHAR_PTR(spool->pool)
&& CHAR_PTR(shdr) <= (CHAR_PTR(spool->pool)+spool->pool_size))
return 1;
return 0;
}

static int smalloc_valid_tag(struct smalloc_hdr *shdr)
{
char *s;
uintptr_t r = smalloc_mktag(shdr);
size_t x;

if (shdr->tag == r) {
s = CHAR_PTR(HEADER_TO_USER(shdr));
s += shdr->usz;
for (x = 0; x < sizeof(struct smalloc_hdr); x += sizeof(uintptr_t)) {
r = smalloc_uinthash(r);
if (memcmp(s+x, &r, sizeof(uintptr_t)) != 0) return 0;
}
s += x; x = 0;
while (x < shdr->rsz - shdr->usz) {
if (s[x] != '\xFF') return 0;
x++;
}
return 1;
}
return 0;
}

static void smalloc_do_crash(struct smalloc_pool *spool, const void *p)
{
char *c = NULL;
*c = 'X';
}

smalloc_ub_handler smalloc_UB = smalloc_do_crash;

void sm_set_ub_handler(smalloc_ub_handler handler)
{
if (!handler) smalloc_UB = smalloc_do_crash;
else smalloc_UB = handler;
}

int smalloc_is_alloc(struct smalloc_pool *spool, struct smalloc_hdr *shdr)
{
if (!smalloc_check_bounds(spool, shdr)) return 0;
if (shdr->rsz == 0) return 0;
if (shdr->rsz > SIZE_MAX) return 0;
if (shdr->usz > SIZE_MAX) return 0;
if (shdr->usz > shdr->rsz) return 0;
if (shdr->rsz % HEADER_SZ) return 0;
if (!smalloc_valid_tag(shdr)) return 0;
return 1;
}

+ 19
- 0
teensy4/sm_zalloc.c Целия файл

@@ -0,0 +1,19 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#include "smalloc_i.h"

void *sm_zalloc_pool(struct smalloc_pool *spool, size_t n)
{
void *r = sm_malloc_pool(spool, n);
if (r) memset(r, 0, n);
return r;
}

void *sm_zalloc(size_t n)
{
return sm_zalloc_pool(&smalloc_curr_pool, n);
}

+ 77
- 0
teensy4/smalloc.h Целия файл

@@ -0,0 +1,77 @@
/*
* SMalloc -- a *static* memory allocator.
*
* See README for a complete description.
*
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
* Written during Aug2017.
*/

#ifndef _SMALLOC_H
#define _SMALLOC_H

#include <stddef.h>
#include <stdint.h>

struct smalloc_pool;

typedef size_t (*smalloc_oom_handler)(struct smalloc_pool *, size_t);

/* describes static pool, if you're going to use multiple pools at same time */
struct smalloc_pool {
void *pool; /* pointer to your pool */
size_t pool_size; /* it's size. Must be aligned with sm_align_pool. */
int do_zero; /* zero pool before use and all the new allocations from it. */
smalloc_oom_handler oomfn; /* this will be called, if non-NULL, on OOM condition in pool */
};

/* a default one which is initialised with sm_set_default_pool. */
extern struct smalloc_pool smalloc_curr_pool;

/* undefined behavior handler is called on typical malloc UB situations */
typedef void (*smalloc_ub_handler)(struct smalloc_pool *, const void *);

void sm_set_ub_handler(smalloc_ub_handler);

int sm_align_pool(struct smalloc_pool *);
int sm_set_pool(struct smalloc_pool *, void *, size_t, int, smalloc_oom_handler);
int sm_set_default_pool(void *, size_t, int, smalloc_oom_handler);
int sm_release_pool(struct smalloc_pool *);
int sm_release_default_pool(void);

/* Use these with multiple pools which you control */

void *sm_malloc_pool(struct smalloc_pool *, size_t);
void *sm_zalloc_pool(struct smalloc_pool *, size_t);
void sm_free_pool(struct smalloc_pool *, void *);

void *sm_realloc_pool(struct smalloc_pool *, void *, size_t);
void *sm_realloc_move_pool(struct smalloc_pool *, void *, size_t);
void *sm_calloc_pool(struct smalloc_pool *, size_t, size_t);

int sm_alloc_valid_pool(struct smalloc_pool *spool, const void *p);

size_t sm_szalloc_pool(struct smalloc_pool *, const void *);
int sm_malloc_stats_pool(struct smalloc_pool *, size_t *, size_t *, size_t *, int *);

/* Use these when you use just default smalloc_curr_pool pool */

void *sm_malloc(size_t);
void *sm_zalloc(size_t); /* guarantee zero memory allocation */
void sm_free(void *);

void *sm_realloc(void *, size_t);
void *sm_realloc_move(void *, size_t);
void *sm_calloc(size_t, size_t); /* calls zalloc internally */

int sm_alloc_valid(const void *p); /* verify pointer without intentional crash */

size_t sm_szalloc(const void *); /* get size of allocation */
/*
* get stats: total used, user used, total free, nr. of allocated blocks.
* any of pointers maybe set to NULL, but at least one must be non NULL.
*/
int sm_malloc_stats(size_t *, size_t *, size_t *, int *);

#endif

+ 40
- 0
teensy4/smalloc_i.h Целия файл

@@ -0,0 +1,40 @@
/*
* This file is a part of SMalloc.
* SMalloc is MIT licensed.
* Copyright (c) 2017 Andrey Rys.
*/

#ifndef _SMALLOC_I_H
#define _SMALLOC_I_H

#include "smalloc.h"
#include <string.h>
#include <limits.h>
#include <errno.h>

struct smalloc_hdr {
size_t rsz; /* real allocated size with overhead (if any) */
size_t usz; /* exact user size as reported by s_szalloc */
uintptr_t tag; /* sum of all the above, hashed value */
};

#define HEADER_SZ (sizeof(struct smalloc_hdr))
#define MIN_POOL_SZ (HEADER_SZ*20)

#define VOID_PTR(p) ((void *)p)
#define CHAR_PTR(p) ((char *)p)
#define PTR_UINT(p) ((uintptr_t)VOID_PTR(p))
#define HEADER_PTR(p) ((struct smalloc_hdr *)p)
#define USER_TO_HEADER(p) (HEADER_PTR((CHAR_PTR(p)-HEADER_SZ)))
#define HEADER_TO_USER(p) (VOID_PTR((CHAR_PTR(p)+HEADER_SZ)))

extern smalloc_ub_handler smalloc_UB;

uintptr_t smalloc_uinthash(uintptr_t x);
uintptr_t smalloc_mktag(struct smalloc_hdr *shdr);
int smalloc_verify_pool(struct smalloc_pool *spool);
int smalloc_is_alloc(struct smalloc_pool *spool, struct smalloc_hdr *shdr);

void *sm_realloc_pool_i(struct smalloc_pool *spool, void *p, size_t n, int nomove);

#endif

Loading…
Отказ
Запис