mirror of
https://github.com/openharmony/third_party_sane-airscan.git
synced 2026-06-30 21:17:55 -04:00
324 lines
6.5 KiB
C
324 lines
6.5 KiB
C
/* AirScan (a.k.a. eSCL) backend for SANE
|
|
*
|
|
* Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
|
|
* See LICENSE for license terms and conditions
|
|
*
|
|
* Memory allocation and strings
|
|
*/
|
|
|
|
#include "airscan.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/******************** Memory allocation ********************/
|
|
|
|
/* While memory block grows, its size is doubled until
|
|
* MEM_CAP_BLOCK is reached, then it grows linearly,
|
|
* reminding a multiple of whole MEM_CAP_BLOCKs
|
|
*/
|
|
#define MEM_CAP_BLOCK 65536
|
|
|
|
/* Each memory block allocated from here has the following
|
|
* control record
|
|
*/
|
|
typedef struct {
|
|
uint32_t len, cap; /* Block length and capacity in bytes */
|
|
} mem_head;
|
|
|
|
/* Check for OOM
|
|
*/
|
|
#define mem_check_oom(p,must) \
|
|
do{ \
|
|
if ((p) == NULL && must) { \
|
|
log_panic(NULL, "Out of memory"); \
|
|
__builtin_unreachable(); \
|
|
} \
|
|
} while(0)
|
|
|
|
/* Truncate the vector length, preserving previously allocated buffer
|
|
*/
|
|
void
|
|
mem_trunc (void *p)
|
|
{
|
|
((mem_head*) p)[-1].len = 0;
|
|
}
|
|
|
|
/* Free memory, previously obtained from mem_new()/mem_expand()
|
|
*/
|
|
void
|
|
mem_free (void *p)
|
|
{
|
|
if (p != NULL) {
|
|
free(((mem_head*) p) - 1);
|
|
}
|
|
}
|
|
|
|
/* Get memory block length, in bytes
|
|
*/
|
|
size_t
|
|
mem_len_bytes (const void *p)
|
|
{
|
|
return p ? ((mem_head*) p)[-1].len : 0;
|
|
}
|
|
|
|
/* Get memory block capacity, in bytes
|
|
*/
|
|
size_t
|
|
mem_cap_bytes (const void *p)
|
|
{
|
|
return p ? ((mem_head*) p)[-1].cap : 0;
|
|
}
|
|
|
|
/* Compute allocation size, including mem_head header, in bytes
|
|
*/
|
|
static inline int
|
|
mem_alloc_size(size_t len, size_t extra, size_t elsize)
|
|
{
|
|
size_t sz = sizeof(mem_head) + elsize * (len + extra);
|
|
|
|
if (sz < MEM_CAP_BLOCK) {
|
|
/* Round up to the next power of 2 */
|
|
sz --;
|
|
sz |= sz >> 1;
|
|
sz |= sz >> 2;
|
|
sz |= sz >> 4;
|
|
sz |= sz >> 8;
|
|
sz |= sz >> 16;
|
|
sz ++;
|
|
} else {
|
|
/* Round up to the next block boundary */
|
|
sz += MEM_CAP_BLOCK - 1;
|
|
sz &= ~(MEM_CAP_BLOCK - 1);
|
|
}
|
|
|
|
return sz;
|
|
}
|
|
|
|
/* Helper function: allocate new block of memory
|
|
*/
|
|
void*
|
|
__mem_alloc (size_t len, size_t extra, size_t elsize, bool must)
|
|
{
|
|
size_t sz = mem_alloc_size(len, extra, elsize);
|
|
mem_head *h = calloc(sz, 1);
|
|
|
|
if (h == NULL) {
|
|
mem_check_oom(h, must);
|
|
return NULL;
|
|
}
|
|
|
|
h->len = len * elsize;
|
|
h->cap = sz - (sizeof(mem_head));
|
|
|
|
return h + 1;
|
|
}
|
|
|
|
/* Helper function for memory allocation.
|
|
* Allocated or resizes memory block
|
|
*/
|
|
void*
|
|
__mem_resize (void *p, size_t len, size_t extra, size_t elsize, bool must)
|
|
{
|
|
size_t sz;
|
|
mem_head *h;
|
|
|
|
/* If `p' is NULL, just call __mem_alloc() */
|
|
if (p == NULL) {
|
|
return __mem_alloc(len, extra, elsize, must);
|
|
}
|
|
|
|
/* Reallocate memory, if required */
|
|
h = ((mem_head*) p) - 1;
|
|
sz = mem_alloc_size(len, extra, elsize);
|
|
|
|
if (h->cap + sizeof(mem_head) < sz) {
|
|
h = realloc(h, sz);
|
|
if (h == NULL) {
|
|
mem_check_oom(h, must);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Zero-fill newly added elements */
|
|
len *= elsize;
|
|
if (len > h->len) {
|
|
memset((char*) (h + 1) + h->len, 0, len - h->len);
|
|
}
|
|
|
|
/* Update control header and return a block */
|
|
h->len = len;
|
|
h->cap = sz - (sizeof(mem_head));
|
|
|
|
return h + 1;
|
|
}
|
|
|
|
/* Helper function for memory allocation.
|
|
* Shrinks memory block
|
|
*/
|
|
void
|
|
__mem_shrink (void *p, size_t len, size_t elsize)
|
|
{
|
|
mem_head *h = ((mem_head*) p) - 1;
|
|
|
|
len *= elsize;
|
|
log_assert(NULL, len <= h->len);
|
|
h->len = len;
|
|
}
|
|
|
|
/******************** Strings ********************/
|
|
/* Create new string as a lowercase copy of existent string
|
|
*/
|
|
char*
|
|
str_dup_tolower (const char *s1)
|
|
{
|
|
char *s = str_dup(s1);
|
|
size_t i;
|
|
|
|
for (i = 0; s[i]; i ++) {
|
|
s[i] = safe_tolower(s[i]);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Create new string and print to it
|
|
*/
|
|
char*
|
|
str_printf (const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
char *s;
|
|
|
|
va_start(ap, format);
|
|
s = str_append_vprintf(NULL, format, ap);
|
|
va_end(ap);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Create new string and print to it, va_list version
|
|
*/
|
|
char*
|
|
str_vprintf (const char *format, va_list ap)
|
|
{
|
|
return str_append_vprintf(NULL, format, ap);
|
|
}
|
|
|
|
/* Append formatted string to string
|
|
*
|
|
* `s' must be previously created by some of str_XXX functions,
|
|
* `s' will be consumed and the new pointer will be returned
|
|
*/
|
|
char*
|
|
str_append_printf (char *s, const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
s = str_append_vprintf(s, format, ap);
|
|
va_end(ap);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Append formatted string to string -- va_list version
|
|
*/
|
|
char*
|
|
str_append_vprintf (char *s, const char *format, va_list ap)
|
|
{
|
|
char buf[4096];
|
|
size_t len, oldlen;
|
|
va_list ap2;
|
|
|
|
va_copy(ap2, ap);
|
|
len = vsnprintf(buf, sizeof(buf), format, ap2);
|
|
va_end(ap2);
|
|
|
|
if (len < sizeof(buf)) {
|
|
return str_append_mem(s, buf, len);
|
|
}
|
|
|
|
oldlen = mem_len(s);
|
|
s = mem_resize(s, oldlen + len, 1);
|
|
|
|
va_copy(ap2, ap);
|
|
vsnprintf(s + oldlen, len + 1, format, ap2);
|
|
va_end(ap2);
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Concatenate several strings. Last pointer must be NULL.
|
|
* The returned pointer must be eventually freed by mem_free
|
|
*/
|
|
char*
|
|
str_concat (const char *s, ...)
|
|
{
|
|
va_list ap;
|
|
char *ret = str_dup(s);
|
|
|
|
va_start(ap, s);
|
|
while ((s = va_arg(ap, const char*)) != NULL) {
|
|
ret = str_append(ret, s);
|
|
}
|
|
va_end(ap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Check if string has a specified prefix
|
|
*/
|
|
bool
|
|
str_has_prefix (const char *s, const char *prefix)
|
|
{
|
|
size_t l1 = strlen(s);
|
|
size_t l2 = strlen(prefix);
|
|
return l1 >= l2 && !memcmp(s, prefix, l2);
|
|
}
|
|
|
|
/* Check if string has a specified suffix
|
|
*/
|
|
bool
|
|
str_has_suffix (const char *s, const char *suffix)
|
|
{
|
|
size_t l1 = strlen(s);
|
|
size_t l2 = strlen(suffix);
|
|
return l1 >= l2 && !memcmp(s + (l1 - l2), suffix, l2);
|
|
}
|
|
|
|
/* Remove leading and trailing white space.
|
|
* This function modifies string in place, and returns pointer
|
|
* to original string, for convenience
|
|
*/
|
|
char*
|
|
str_trim (char *s)
|
|
{
|
|
size_t len = strlen(s), skip;
|
|
|
|
while (len > 0 && safe_isspace(s[len - 1])) {
|
|
len --;
|
|
}
|
|
|
|
for (skip = 0; skip < len && safe_isspace(s[skip]); skip ++) {
|
|
;
|
|
}
|
|
|
|
len -= skip;
|
|
if (len != 0 && skip != 0) {
|
|
memmove(s, s + skip, len);
|
|
}
|
|
s[len] = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
/* vim:ts=8:sw=4:et
|
|
*/
|
|
|