2022-05-03 03:56:07 +00:00
|
|
|
#include "hg_ipc.h"
|
2022-05-30 03:04:26 +00:00
|
|
|
#include "../wrapper/hg_log.h"
|
2022-05-03 03:56:07 +00:00
|
|
|
#include "huagao/hgscanner_error.h"
|
|
|
|
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-06-16 08:03:55 +00:00
|
|
|
//#include "scanner_manager.h"
|
2022-05-03 03:56:07 +00:00
|
|
|
#else
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
2022-06-01 03:04:10 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2022-05-03 03:56:07 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2022-06-28 09:16:03 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// memory management ...
|
|
|
|
void* allocate_memory(size_t bytes, const char* log_msg)
|
|
|
|
{
|
|
|
|
bytes += 7;
|
|
|
|
bytes /= 8;
|
|
|
|
bytes *= 8;
|
|
|
|
|
|
|
|
return new char[bytes];
|
|
|
|
}
|
|
|
|
void free_memory(void* ptr)
|
|
|
|
{
|
|
|
|
if (ptr)
|
|
|
|
delete[] ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-05-03 03:56:07 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// windows event ...
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-05-03 03:56:07 +00:00
|
|
|
int __stdcall sem_init(sem_t* handle, int, int)
|
|
|
|
{
|
|
|
|
if (!handle)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*handle = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if (*handle)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
errno = GetLastError();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void __stdcall sem_destroy(sem_t* handle)
|
|
|
|
{
|
|
|
|
if (*handle)
|
|
|
|
{
|
|
|
|
CloseHandle(*handle);
|
|
|
|
*handle = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int __stdcall sem_trywait(sem_t* handle)
|
|
|
|
{
|
|
|
|
return WaitForSingleObject(*handle, 1) == WAIT_TIMEOUT ? -1 : 0;
|
|
|
|
}
|
|
|
|
void __stdcall sem_wait(sem_t* handle)
|
|
|
|
{
|
|
|
|
if(WaitForSingleObject(*handle, INFINITE) == WAIT_OBJECT_0)
|
|
|
|
ResetEvent(*handle);
|
|
|
|
}
|
|
|
|
int __stdcall sem_timedwait(sem_t* handle, struct timespec* to)
|
|
|
|
{
|
|
|
|
DWORD elapse = to->tv_sec * 1000;
|
|
|
|
elapse += to->tv_nsec / (1000 * 1000);
|
|
|
|
|
|
|
|
int ret = WaitForSingleObject(*handle, elapse) == WAIT_TIMEOUT ? -1 : 0;
|
|
|
|
|
|
|
|
if(ret == 0)
|
|
|
|
ResetEvent(*handle);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
void __stdcall sem_post(sem_t* handle)
|
|
|
|
{
|
|
|
|
SetEvent(*handle);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define pid_t int
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// platform_event (base on semaphore)
|
|
|
|
platform_event::platform_event() : waiting_(false), dbg_info_("")
|
|
|
|
{
|
|
|
|
int err = sem_init(&sem_, 0, 0);
|
|
|
|
|
|
|
|
if (err == -1)
|
|
|
|
{
|
|
|
|
err = errno;
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_FATAL, "(%s)sem_init failed: %d\n", hg_log::format_ptr(this).c_str(), err);
|
2022-05-03 03:56:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
platform_event::~platform_event()
|
|
|
|
{
|
|
|
|
sem_destroy(&sem_);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool platform_event::try_wait(void)
|
|
|
|
{
|
|
|
|
return sem_trywait(&sem_) == 0;
|
|
|
|
}
|
|
|
|
bool platform_event::wait(unsigned timeout)
|
|
|
|
{
|
|
|
|
bool waited = true;
|
|
|
|
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "platform_event(%s - %s) --> waiting...\n", hg_log::format_ptr(this).c_str(), dbg_info_.c_str());
|
2022-05-03 03:56:07 +00:00
|
|
|
waiting_ = true;
|
|
|
|
if (timeout == USB_TIMEOUT_INFINITE)
|
|
|
|
sem_wait(&sem_);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
struct timespec to;
|
|
|
|
to.tv_sec = timeout / 1000;
|
|
|
|
to.tv_nsec = (long)((timeout % 1000) * 1000 * 1000);
|
|
|
|
waited = sem_timedwait(&sem_, &to) == 0;
|
|
|
|
}
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "platform_event(%s - %s) --> %s.\n", hg_log::format_ptr(this).c_str(), dbg_info_.c_str(), waited ? "waited" : "wait timeout");
|
2022-05-03 03:56:07 +00:00
|
|
|
waiting_ = false;
|
|
|
|
|
|
|
|
return waited;
|
|
|
|
}
|
|
|
|
void platform_event::notify(void)
|
|
|
|
{
|
|
|
|
sem_post(&sem_);
|
|
|
|
}
|
|
|
|
bool platform_event::is_waiting(void)
|
|
|
|
{
|
|
|
|
return waiting_;
|
|
|
|
}
|
|
|
|
void platform_event::set_debug_info(const char* info)
|
|
|
|
{
|
|
|
|
dbg_info_ = info ? info : "";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// shared_memory
|
|
|
|
shared_memory::shared_memory(unsigned long long key, size_t size) : key_(key), obj_((void*)-1), first_(true), bytes_(size), len_(0)
|
|
|
|
{
|
|
|
|
unsigned int* ptr = (unsigned int*)&key_;
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "shared memory key = 0x%x%08x\n", ptr[1], ptr[0]);
|
2022-05-03 03:56:07 +00:00
|
|
|
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
shared_memory::~shared_memory()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void shared_memory::init(void)
|
|
|
|
{
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-05-03 03:56:07 +00:00
|
|
|
char name[40] = { 0 };
|
|
|
|
DWORD* key = (DWORD*)&key_;
|
|
|
|
HANDLE h = NULL;
|
|
|
|
|
|
|
|
sprintf(name, "scanner_0x%08x-%08x", key[1], key[0]);
|
|
|
|
h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, bytes_, name);
|
|
|
|
if (h == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
first_ = !(GetLastError() == ERROR_ALREADY_EXISTS);
|
|
|
|
obj_ = (void*)h;
|
|
|
|
#else
|
|
|
|
int obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600);
|
|
|
|
if (obj < 0)
|
|
|
|
{
|
2022-05-16 09:26:36 +00:00
|
|
|
unsigned int* v = (unsigned int*)&key_;
|
2022-05-03 03:56:07 +00:00
|
|
|
if (errno == EEXIST)
|
|
|
|
{
|
|
|
|
first_ = false;
|
|
|
|
obj = shmget(key_, bytes_, 0600);
|
2022-05-16 09:26:36 +00:00
|
|
|
if(obj == -1)
|
|
|
|
obj = shmget(key_, bytes_, 0);
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "open existing: shmget(0x%x%08x) = %d\n", v[1], v[0], obj);
|
2022-05-16 09:26:36 +00:00
|
|
|
obj_ = (void*)obj;
|
|
|
|
|
|
|
|
std::string prev(read()), proc("");
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "shared memory content: %s\n", prev.c_str());
|
2022-05-16 09:26:36 +00:00
|
|
|
if(prev.length())
|
|
|
|
{
|
|
|
|
proc = prev;
|
|
|
|
size_t pos = proc.find("pid: ");
|
|
|
|
if (pos != std::string::npos)
|
|
|
|
proc.erase(0, pos + 5);
|
|
|
|
pos = proc.find(")");
|
|
|
|
if (pos != std::string::npos)
|
|
|
|
proc.erase(pos);
|
|
|
|
proc = shared_memory::get_proc_name_by_pid(atoi(proc.c_str()));
|
|
|
|
if (proc.length())
|
|
|
|
{
|
|
|
|
pos = prev.find("(");
|
|
|
|
if (pos == std::string::npos)
|
|
|
|
pos = prev.length();
|
|
|
|
if (strcasecmp(proc.c_str(), prev.substr(0, pos).c_str()))
|
|
|
|
proc = "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (proc.empty())
|
2022-05-03 03:56:07 +00:00
|
|
|
{
|
|
|
|
first_ = true;
|
|
|
|
clear();
|
|
|
|
obj = shmget(key_, bytes_, IPC_EXCL | IPC_CREAT | 0600);
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_1(LOG_LEVEL_DEBUG_INFO, "%s is not existing and reopen it\n", prev.c_str());
|
2022-05-03 03:56:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2022-05-16 09:26:36 +00:00
|
|
|
{
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "shmget(0x%x%08x) = %d\n", v[1], v[0], errno);
|
2022-05-03 03:56:07 +00:00
|
|
|
return;
|
2022-05-16 09:26:36 +00:00
|
|
|
}
|
2022-05-03 03:56:07 +00:00
|
|
|
}
|
|
|
|
obj_ = (void*)obj;
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "shared memory id = %d[%s], \n", obj, first_ ? "created" : "opened");
|
2022-05-03 03:56:07 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if(first_)
|
|
|
|
{
|
|
|
|
pid_t pid = getpid();
|
|
|
|
std::string me("");
|
|
|
|
char buf[40] = { 0 };
|
|
|
|
unsigned int* pn = (unsigned int*)&pid;
|
|
|
|
|
|
|
|
if (sizeof(pid) > 4 && pn[1])
|
|
|
|
sprintf(buf, "(pid: 0x%x%08x)", pn[1], pn[0]);
|
|
|
|
else
|
|
|
|
sprintf(buf, "(pid: %u)", pn[0]);
|
|
|
|
hg_log::pe_path(&me);
|
|
|
|
me += buf;
|
|
|
|
write(me.c_str(), me.length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void shared_memory::clear(void)
|
|
|
|
{
|
|
|
|
if (obj_ != (void*)-1)
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-05-03 03:56:07 +00:00
|
|
|
CloseHandle((HANDLE)obj_);
|
|
|
|
#else
|
|
|
|
{
|
|
|
|
if (first_)
|
|
|
|
{
|
|
|
|
struct shmid_ds ds = { 0 };
|
|
|
|
int* h = (int*)&obj_;
|
|
|
|
shmctl(*h, IPC_RMID, &ds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
obj_ = (void*)-1;
|
|
|
|
}
|
|
|
|
char* shared_memory::get_buf(void)
|
|
|
|
{
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-05-03 03:56:07 +00:00
|
|
|
char* buf = (char*)MapViewOfFile((HANDLE)obj_, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
|
|
|
|
if (!buf)
|
|
|
|
buf = (char*)-1;
|
|
|
|
#else
|
|
|
|
int* h = (int*)&obj_;
|
|
|
|
char* buf = (char*)shmat(*h, 0, 0);
|
2022-06-01 08:11:51 +00:00
|
|
|
VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "shared memory %d buffer = %s, error = %d\n", *h, hg_log::format_ptr(buf).c_str(), errno);
|
2022-05-03 03:56:07 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
void shared_memory::release_buf(void* buf)
|
|
|
|
{
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-05-03 03:56:07 +00:00
|
|
|
UnmapViewOfFile(buf);
|
|
|
|
#else
|
|
|
|
shmdt(buf);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-07-18 08:56:03 +00:00
|
|
|
#if !defined(WIN32) && !defined(_WIN64)
|
2022-05-16 09:26:36 +00:00
|
|
|
std::string shared_memory::get_proc_name_by_pid(pid_t pid)
|
|
|
|
{
|
|
|
|
char path[512] = { 0 };
|
|
|
|
unsigned int* v = (unsigned int*)&pid;
|
|
|
|
std::string ret("");
|
|
|
|
|
|
|
|
if (sizeof(pid) > 4 && v[1])
|
|
|
|
sprintf(path, "/proc/%lld/status", pid);
|
|
|
|
else
|
|
|
|
sprintf(path, "/proc/%u/status", pid);
|
|
|
|
|
|
|
|
FILE* src = fopen(path, "rb");
|
|
|
|
if (src)
|
|
|
|
{
|
|
|
|
char val[512] = { 0 };
|
|
|
|
|
|
|
|
memset(path, 0, sizeof(path));
|
|
|
|
fgets(path, sizeof(path) - 1, src);
|
|
|
|
fclose(src);
|
|
|
|
sscanf(path, "%*s %s", val);
|
|
|
|
ret = val;
|
|
|
|
}
|
|
|
|
if (sizeof(pid) > 4 && v[1])
|
|
|
|
{
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "PID(%lld) name is: %s\n", pid, ret.c_str());
|
2022-05-16 09:26:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-05-30 03:04:26 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "PID(%u) name is: %s\n", pid, ret.c_str());
|
2022-05-16 09:26:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-05-03 03:56:07 +00:00
|
|
|
bool shared_memory::is_ok(void)
|
|
|
|
{
|
2022-09-26 09:52:52 +00:00
|
|
|
return obj_ != (void*)-1;
|
2022-05-03 03:56:07 +00:00
|
|
|
}
|
|
|
|
bool shared_memory::is_first(void)
|
|
|
|
{
|
|
|
|
return is_ok() && first_;
|
|
|
|
}
|
|
|
|
std::string shared_memory::read(void)
|
|
|
|
{
|
|
|
|
if (obj_ == (void*)-1)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
char* buf = get_buf();
|
|
|
|
if (buf == (char*)-1)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
std::string ret("");
|
|
|
|
size_t len = 0;
|
2022-09-22 08:23:16 +00:00
|
|
|
int off = 4; // sizeof(size_t);
|
2022-05-03 03:56:07 +00:00
|
|
|
|
|
|
|
memcpy(&len, buf, off);
|
|
|
|
ret = std::string(buf + off, len);
|
|
|
|
release_buf(buf);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
int shared_memory::write(const char* data, size_t len)
|
|
|
|
{
|
|
|
|
if (len > bytes_)
|
2022-05-17 07:04:55 +00:00
|
|
|
return SCANNER_ERR_INSUFFICIENT_MEMORY;
|
2022-05-03 03:56:07 +00:00
|
|
|
|
|
|
|
char* buf = get_buf();
|
2022-09-22 08:23:16 +00:00
|
|
|
int off = 4; // sizeof(len);
|
2022-05-03 03:56:07 +00:00
|
|
|
if (buf == (char*)-1)
|
|
|
|
return errno;
|
|
|
|
|
|
|
|
memcpy(buf, &len, off);
|
|
|
|
memcpy(buf + off, data, len);
|
|
|
|
len_ = len;
|
|
|
|
release_buf(buf);
|
2022-10-28 06:51:52 +00:00
|
|
|
|
|
|
|
return 0;
|
2022-05-03 03:56:07 +00:00
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// tiny_file_map ...
|
2022-06-06 04:03:24 +00:00
|
|
|
tiny_file_map::tiny_file_map() : size_(0), map_(INVALID_HANDLE_NAME), buf_(nullptr), file_(""), keep_f_(false)
|
|
|
|
, map_off_(0), map_bytes_(0), page_size_(hg_log::get_page_size())
|
2022-06-16 08:03:55 +00:00
|
|
|
{
|
|
|
|
hg_log::get_page_size(&page_size_);
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
tiny_file_map::~tiny_file_map()
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
HANDLE_NAME tiny_file_map::open_file_for_mapping(const char* file, unsigned* bytes, bool create)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
HANDLE_NAME ret = INVALID_HANDLE_NAME;
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-06-06 04:03:24 +00:00
|
|
|
HANDLE f = INVALID_HANDLE_VALUE;
|
|
|
|
if (create)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (f != INVALID_HANDLE_VALUE)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
DWORD wrote = SetFilePointer(f, *bytes - 1, NULL, FILE_BEGIN);
|
|
|
|
if (wrote != *bytes - 1 || !WriteFile(f, "\0", 1, &wrote, NULL))
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
CloseHandle(f);
|
|
|
|
f = INVALID_HANDLE_VALUE;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
f = CreateFileA(file, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (f != INVALID_HANDLE_VALUE)
|
|
|
|
*bytes = GetFileSize(f, NULL);
|
|
|
|
}
|
|
|
|
if (f != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
ret = CreateFileMapping(f, NULL, PAGE_READWRITE, 0, *bytes, NULL);
|
|
|
|
CloseHandle(f);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (create)
|
|
|
|
{
|
|
|
|
ret = ::open(file, O_CREAT | O_RDWR);
|
|
|
|
if (ret != INVALID_HANDLE_NAME)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
if (lseek(ret, *bytes - 1, SEEK_SET) < 0)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "set file size to %u - 1 bytes failed: %d\n", *bytes, errno);
|
|
|
|
::close(ret);
|
|
|
|
remove(file);
|
|
|
|
ret = INVALID_HANDLE_NAME;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
if (write(ret, "0", 1) < 0)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
VLOG_MINI_2(LOG_LEVEL_DEBUG_INFO, "set file size to %u bytes failed: %d\n", *bytes, errno);
|
|
|
|
::close(ret);
|
|
|
|
remove(file);
|
|
|
|
ret = INVALID_HANDLE_NAME;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
else
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
ret = ::open(file, O_RDWR);
|
|
|
|
if (ret != INVALID_HANDLE_NAME)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
|
|
|
struct stat fsize;
|
2022-06-06 04:03:24 +00:00
|
|
|
if (fstat(ret, &fsize) >= 0)
|
|
|
|
*bytes = fsize.st_size;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
void tiny_file_map::close_handle_name(HANDLE_NAME h)
|
|
|
|
{
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-06-06 04:03:24 +00:00
|
|
|
CloseHandle(h);
|
|
|
|
#else
|
|
|
|
::close(h);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
void* tiny_file_map::sys_map_api(HANDLE_NAME h, int access, unsigned int off, unsigned size, int* err)
|
|
|
|
{
|
|
|
|
void* mem = nullptr;
|
|
|
|
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-06-06 04:03:24 +00:00
|
|
|
mem = MapViewOfFile(h, access, 0, off, size);
|
|
|
|
if (err)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
if (mem)
|
|
|
|
*err = SCANNER_ERR_OK;
|
|
|
|
else
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
|
|
|
|
*err = SCANNER_ERR_INSUFFICIENT_MEMORY;
|
|
|
|
else
|
|
|
|
*err = SCANNER_ERR_OUT_OF_RANGE;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
#else
|
|
|
|
mem = mmap(nullptr, size, access, MAP_SHARED, h, off);
|
|
|
|
if (mem == MAP_FAILED)
|
|
|
|
{
|
|
|
|
mem = nullptr;
|
|
|
|
if (errno == ENOMEM)
|
|
|
|
*err = SCANNER_ERR_INSUFFICIENT_MEMORY;
|
|
|
|
else
|
|
|
|
*err = SCANNER_ERR_OUT_OF_RANGE;
|
|
|
|
}
|
|
|
|
else if(err)
|
|
|
|
*err = SCANNER_ERR_OK;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
}
|
|
|
|
void tiny_file_map::sys_unmap_api(void* buf, size_t size)
|
|
|
|
{
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-06-06 04:03:24 +00:00
|
|
|
UnmapViewOfFile(buf);
|
|
|
|
#else
|
|
|
|
munmap(buf, size);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int tiny_file_map::map_to_mem(unsigned int off)
|
|
|
|
{
|
|
|
|
int err = SCANNER_ERR_OUT_OF_RANGE;
|
|
|
|
|
2022-07-18 08:56:03 +00:00
|
|
|
#if defined(WIN32) || defined(_WIN64)
|
2022-06-06 04:03:24 +00:00
|
|
|
int acc = FILE_MAP_READ | FILE_MAP_WRITE;
|
|
|
|
#else
|
|
|
|
int acc = PROT_READ | PROT_WRITE;
|
|
|
|
#endif
|
|
|
|
if (off < size_)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned int bytes = size_ - off;
|
|
|
|
if (off >= map_off_ && off + bytes <= map_off_ + map_bytes_)
|
|
|
|
err = SCANNER_ERR_OK;
|
|
|
|
else
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
if (buf_)
|
|
|
|
tiny_file_map::sys_unmap_api(buf_, map_bytes_);
|
|
|
|
off /= page_size_;
|
|
|
|
off *= page_size_;
|
|
|
|
map_bytes_ = bytes;
|
|
|
|
map_off_ = off;
|
|
|
|
buf_ = (unsigned char*)tiny_file_map::sys_map_api(map_, acc, map_off_, map_bytes_, &err); // MapViewOfFile(map_, FILE_MAP_READ | FILE_MAP_WRITE, 0, off, map_bytes_);
|
|
|
|
if (err != SCANNER_ERR_OK)
|
|
|
|
{
|
|
|
|
map_bytes_ /= page_size_;
|
|
|
|
map_bytes_ *= page_size_;
|
|
|
|
while (map_bytes_ >= page_size_
|
|
|
|
&& !(buf_ = (unsigned char*)tiny_file_map::sys_map_api(map_, acc, map_off_, map_bytes_, &err))
|
|
|
|
&& err == SCANNER_ERR_INSUFFICIENT_MEMORY)
|
|
|
|
map_bytes_ -= page_size_;
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int tiny_file_map::open(const char* file, bool existing, unsigned int size)
|
|
|
|
{
|
|
|
|
int ret = SCANNER_ERR_INSUFFICIENT_MEMORY;
|
|
|
|
|
|
|
|
close();
|
|
|
|
map_ = tiny_file_map::open_file_for_mapping(file, &size, !existing);
|
|
|
|
if (map_ != INVALID_HANDLE_NAME)
|
|
|
|
{
|
|
|
|
ret = SCANNER_ERR_OK;
|
|
|
|
size_ = size;
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
VLOG_MINI_3(LOG_LEVEL_DEBUG_INFO, "map([%s]%s) = %d\n", existing ? "existing" : "new", file, ret);
|
|
|
|
if (ret == SCANNER_ERR_OK)
|
2022-06-01 03:04:10 +00:00
|
|
|
file_ = file;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
void tiny_file_map::close(void)
|
|
|
|
{
|
|
|
|
if (buf_)
|
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
tiny_file_map::sys_unmap_api(buf_, size_);
|
2022-06-01 03:04:10 +00:00
|
|
|
buf_ = nullptr;
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
if (map_ != INVALID_HANDLE_NAME)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
close_handle_name(map_);
|
|
|
|
map_ = INVALID_HANDLE_NAME;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
if (!keep_f_ && !file_.empty())
|
|
|
|
remove(file_.c_str());
|
|
|
|
|
|
|
|
size_ = 0;
|
|
|
|
file_ = "";
|
|
|
|
keep_f_ = false;
|
2022-06-06 04:03:24 +00:00
|
|
|
map_off_ = map_bytes_ = 0;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
void tiny_file_map::keep_file(bool keep)
|
|
|
|
{
|
|
|
|
keep_f_ = keep;
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned char* tiny_file_map::mapping_buffer(unsigned int off, unsigned int* bytes)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned int len = bytes ? *bytes : size_;
|
|
|
|
unsigned char* buf = nullptr;
|
|
|
|
|
|
|
|
if (off >= size_)
|
|
|
|
{
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
if (!buf_ && map_to_mem(off) != SCANNER_ERR_OK)
|
|
|
|
{
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (off >= map_off_ && off + len <= map_off_ + map_bytes_)
|
|
|
|
{
|
|
|
|
buf = buf_ + off - map_off_;
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (off < map_off_ || off >= map_off_ + map_bytes_)
|
|
|
|
{
|
|
|
|
if (map_to_mem(off) == SCANNER_ERR_OK)
|
|
|
|
{
|
|
|
|
buf = buf_ + off - map_off_;
|
|
|
|
if (bytes)
|
|
|
|
*bytes = map_bytes_ - (off - map_off_);
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
buf = buf_ + off - map_off_;
|
|
|
|
if (bytes)
|
|
|
|
*bytes = map_bytes_ - (off - map_off_);
|
|
|
|
|
|
|
|
return buf;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
std::string tiny_file_map::file(void)
|
|
|
|
{
|
|
|
|
return file_;
|
|
|
|
}
|
|
|
|
unsigned int tiny_file_map::size(void)
|
|
|
|
{
|
|
|
|
return size_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tiny_file_map::swap(void)
|
|
|
|
{
|
|
|
|
bool ret = true;
|
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
tiny_file_map::sys_unmap_api(buf_, size_);
|
|
|
|
buf_ = nullptr;
|
2022-06-01 03:04:10 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// memory or file mapping ...
|
|
|
|
tiny_buffer::tiny_buffer(unsigned int size
|
|
|
|
, const char* tmp_path
|
|
|
|
, const char* name_leading
|
|
|
|
, const char* ext
|
|
|
|
, unsigned int uniq_id)
|
2022-07-23 05:42:34 +00:00
|
|
|
: size_(size), buf_(nullptr), img_statu_(SANE_Image_Statu_OK)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
|
|
|
init(tmp_path, name_leading, ext, uniq_id);
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
tiny_buffer::tiny_buffer(const char* src_file) : size_(0), buf_(nullptr)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
fmap_.open(src_file);
|
2022-06-01 03:04:10 +00:00
|
|
|
size_ = fmap_.size();
|
2022-06-06 04:03:24 +00:00
|
|
|
|
|
|
|
unsigned int len = size_;
|
|
|
|
buf_ = fmap_.mapping_buffer(0, &len);
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
tiny_buffer::~tiny_buffer()
|
|
|
|
{
|
|
|
|
if (buf_)
|
|
|
|
{
|
|
|
|
if (fmap_.file().empty())
|
|
|
|
delete[] buf_;
|
|
|
|
else
|
|
|
|
fmap_.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tiny_buffer::init(const char* tmp_path, const char* name_leading, const char* ext, unsigned int uniq_id)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
buf_ = new unsigned char[size_];
|
|
|
|
memset(buf_, 0, size_);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
if (tmp_path && *tmp_path)
|
|
|
|
{
|
|
|
|
std::string f(tmp_path);
|
|
|
|
char buf[128] = { 0 };
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
f += PATH_SEPARATOR;
|
|
|
|
f += name_leading ? name_leading : "mapf";
|
|
|
|
sprintf(buf, "_%05u.%s", uniq_id, ext ? ext : "tmp");
|
|
|
|
f += buf;
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned int bytes = size_;
|
2022-06-16 08:03:55 +00:00
|
|
|
fmap_.open(f.c_str(), false, size_);
|
2022-06-06 04:03:24 +00:00
|
|
|
buf_ = fmap_.mapping_buffer(0, &bytes);
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int tiny_buffer::size(void)
|
|
|
|
{
|
|
|
|
return size_;
|
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned char* tiny_buffer::data(unsigned int off, unsigned int* bytes)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
if (off >= size_)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (fmap_.file().empty())
|
|
|
|
{
|
|
|
|
if (size_ - off < *bytes)
|
|
|
|
*bytes = size_ - off;
|
|
|
|
|
|
|
|
return buf_ + off;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fmap_.mapping_buffer(off, bytes);
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
|
|
|
void tiny_buffer::keep_file(bool keep)
|
|
|
|
{
|
|
|
|
fmap_.keep_file(keep);
|
|
|
|
}
|
|
|
|
std::string tiny_buffer::file(void)
|
|
|
|
{
|
|
|
|
return fmap_.file();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tiny_buffer::swap(void)
|
|
|
|
{
|
|
|
|
if (fmap_.file().empty())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
bool ret = fmap_.swap();
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned int bytes = size_;
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-06-06 04:03:24 +00:00
|
|
|
buf_ = fmap_.mapping_buffer(0, &bytes);
|
2022-06-01 03:04:10 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2022-07-19 09:12:06 +00:00
|
|
|
int tiny_buffer::to_file(const char* file)
|
|
|
|
{
|
|
|
|
FILE* dst = fopen(file, "wb");
|
|
|
|
|
|
|
|
if (!dst)
|
|
|
|
return errno;
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-07-19 09:12:06 +00:00
|
|
|
unsigned int off = 0, len = size_;
|
|
|
|
unsigned char* buf = data(off, &len);
|
|
|
|
while (buf)
|
|
|
|
{
|
|
|
|
fwrite(buf, 1, len, dst);
|
|
|
|
off += len;
|
|
|
|
if (off >= size_)
|
|
|
|
break;
|
|
|
|
len = size_ - off;
|
|
|
|
buf = data(off, &len);
|
|
|
|
}
|
|
|
|
fclose(dst);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
|
2022-07-23 05:42:34 +00:00
|
|
|
void tiny_buffer::set_image_statu(int statu)
|
|
|
|
{
|
|
|
|
img_statu_ = statu;
|
|
|
|
}
|
|
|
|
int tiny_buffer::get_image_statu(void)
|
|
|
|
{
|
|
|
|
return img_statu_;
|
|
|
|
}
|
|
|
|
|
2022-06-01 03:04:10 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// final_img_queue
|
|
|
|
|
2022-11-26 02:00:40 +00:00
|
|
|
final_img_queue::final_img_queue() : mem_usage_(0)
|
2022-06-01 03:04:10 +00:00
|
|
|
{}
|
|
|
|
final_img_queue::~final_img_queue()
|
|
|
|
{}
|
|
|
|
|
2022-11-26 02:00:40 +00:00
|
|
|
unsigned long long final_img_queue::mem_usage(void)
|
|
|
|
{
|
|
|
|
return mem_usage_;
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
size_t final_img_queue::size(void)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lck(lock_);
|
|
|
|
|
|
|
|
return queue_.size();
|
|
|
|
}
|
|
|
|
void final_img_queue::clear(void)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lck(lock_);
|
|
|
|
|
2022-11-26 02:00:40 +00:00
|
|
|
mem_usage_ = 0;
|
2022-06-01 03:04:10 +00:00
|
|
|
queue_.clear();
|
|
|
|
}
|
|
|
|
bool final_img_queue::put(int w, int h, int bpp, int channels, int line_bytes, void* data, unsigned bytes
|
2023-01-28 07:19:10 +00:00
|
|
|
, const char* tmp_path, const char* name_leading, const char* ext, int ind, uint32_t id)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
|
|
|
IMGDT imgd;
|
|
|
|
bool ret = false;
|
2022-06-06 04:03:24 +00:00
|
|
|
unsigned int l = bytes, off = 0;
|
2022-06-01 03:04:10 +00:00
|
|
|
|
|
|
|
imgd.header.bits = bpp;
|
|
|
|
imgd.header.bytes = bytes;
|
|
|
|
imgd.header.channels = channels;
|
|
|
|
imgd.header.height = h;
|
|
|
|
imgd.header.line_bytes = line_bytes;
|
|
|
|
imgd.header.width = w;
|
2023-01-28 07:19:10 +00:00
|
|
|
imgd.header.src_id = id;
|
2022-06-01 03:04:10 +00:00
|
|
|
imgd.offset = 0;
|
|
|
|
imgd.data.reset(new tiny_buffer(bytes, tmp_path, name_leading, ext, ind));
|
2022-06-06 04:03:24 +00:00
|
|
|
|
|
|
|
unsigned char* buf = imgd.data->data(off, &l),
|
|
|
|
* src = (unsigned char*)data;
|
|
|
|
while(buf)
|
2022-06-01 03:04:10 +00:00
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
memcpy(buf, src, l);
|
|
|
|
off += l;
|
|
|
|
if (off >= bytes)
|
|
|
|
break;
|
|
|
|
|
|
|
|
src += l;
|
|
|
|
l = bytes - off;
|
|
|
|
buf = imgd.data->data(off, &l);
|
|
|
|
}
|
|
|
|
if (off >= bytes && imgd.data->swap())
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lck(lock_);
|
|
|
|
queue_.push_back(imgd);
|
2022-11-26 02:00:40 +00:00
|
|
|
mem_usage_ += bytes;
|
2022-06-06 04:03:24 +00:00
|
|
|
ret = true;
|
2022-06-01 03:04:10 +00:00
|
|
|
}
|
2022-06-06 04:03:24 +00:00
|
|
|
else
|
|
|
|
imgd.data.reset();
|
2022-06-01 03:04:10 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
bool final_img_queue::front(IMH* header)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lck(lock_);
|
|
|
|
if (queue_.size() == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
memcpy(header, &queue_[0].header, sizeof(*header));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
void final_img_queue::fetch_front(void* buf, int* len, bool* over)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lck(lock_);
|
|
|
|
|
|
|
|
if (queue_.size() == 0)
|
|
|
|
{
|
|
|
|
if(len)
|
|
|
|
*len = 0;
|
|
|
|
if(over)
|
|
|
|
*over = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-06-06 04:03:24 +00:00
|
|
|
// for third-apps, we make fake data upto len when re-map file failed here
|
2022-06-01 03:04:10 +00:00
|
|
|
IMGDT& imgd = queue_[0];
|
|
|
|
|
|
|
|
if (imgd.offset == 0)
|
|
|
|
{
|
|
|
|
if (!imgd.data->swap())
|
|
|
|
{
|
|
|
|
VLOG_MINI_1(LOG_LEVEL_FATAL, "Reload final image '%s' failed!\n", imgd.data->file().c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (imgd.offset + *len >= imgd.header.bytes)
|
|
|
|
*len = imgd.header.bytes - imgd.offset;
|
2022-06-06 04:03:24 +00:00
|
|
|
|
|
|
|
unsigned char* src = imgd.data->data(imgd.offset, (unsigned int*)len);
|
|
|
|
if (src)
|
|
|
|
{
|
|
|
|
memcpy(buf, src, *len);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VLOG_MINI_2(LOG_LEVEL_FATAL, "Remap final image '%s + 0x%08x' failed!\n", imgd.data->file().c_str(), imgd.offset);
|
|
|
|
}
|
2022-06-01 03:04:10 +00:00
|
|
|
imgd.offset += *len;
|
|
|
|
if (imgd.offset >= imgd.header.bytes)
|
|
|
|
{
|
2022-11-26 02:00:40 +00:00
|
|
|
mem_usage_ -= imgd.header.bytes;
|
|
|
|
if (mem_usage_ < 0)
|
|
|
|
mem_usage_ = 0;
|
2022-06-01 03:04:10 +00:00
|
|
|
if (over)
|
|
|
|
*over = true;
|
|
|
|
queue_.erase(queue_.begin());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|