int svx_circlebuf_create(svx_circlebuf_t **self, size_t max_len, size_t min_len, size_t min_step) { if(NULL == self || 0 == min_len || 0 == min_step || (0 != max_len && (min_len > max_len || min_step > max_len))) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, max_len:%zu, min_len:%zu, min_step:%zu\n", self, max_len, min_len, min_step); /* align to 64 bits */ if(0 != max_len % 8) max_len += (8 - max_len % 8); if(0 != min_len % 8) min_len += (8 - min_len % 8); if(0 != min_step % 8) min_step += (8 - min_step % 8); if(NULL == (*self = malloc(sizeof(svx_circlebuf_t)))) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOMEM, NULL); if(NULL == ((*self)->buf = malloc(min_len))) /* create buffer use min length */ { free(*self); *self = NULL; SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOMEM, NULL); } (*self)->size = min_len; (*self)->used = 0; (*self)->max = max_len; (*self)->min = min_len; (*self)->step = min_step; (*self)->offset_r = 0; (*self)->offset_w = 0; return 0; }
int svx_crash_set_timezone_mode(svx_crash_timezone_mode_t mode) { struct timeval tv; struct tm tm; svx_crash_timezone_mode = mode; if(SVX_CRASH_TIMEZONE_MODE_GMT == mode) { svx_crash_timezone_off = 0; strncpy(svx_crash_timezone, "+0000", sizeof(svx_crash_timezone)); svx_crash_timezone[sizeof(svx_crash_timezone) - 1] = '\0'; } else { if(0 != gettimeofday(&tv, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); if(NULL == localtime_r((time_t*)(&(tv.tv_sec)), &tm)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); #ifdef __USE_BSD svx_crash_timezone_off = tm.tm_gmtoff; #else svx_crash_timezone_off = tm.__tm_gmtoff; #endif snprintf(svx_crash_timezone, sizeof(svx_crash_timezone), "%c%02d%02d", svx_crash_timezone_off < 0 ? '-' : '+', abs(svx_crash_timezone_off / 3600), abs(svx_crash_timezone_off % 3600)); } return 0; }
int svx_tcp_connector_create(svx_tcp_connector_t **self, svx_looper_t *looper, svx_inetaddr_t *server_addr, svx_inetaddr_t *client_addr, int64_t init_delay_ms, int64_t max_delay_ms, svx_tcp_connector_connected_callback_t connected_cb, void *connected_cb_arg) { if(NULL == self || NULL == looper || NULL == connected_cb || init_delay_ms < 0 || max_delay_ms < 0 || init_delay_ms > max_delay_ms) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, looper:%p, connected_cb:%p, init_delay_ms:%"PRId64", max_delay_ms:%"PRId64"\n", self, looper, connected_cb, init_delay_ms, max_delay_ms); if(NULL == (*self = malloc(sizeof(svx_tcp_connector_t)))) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOMEM, NULL); (*self)->looper = looper; (*self)->server_addr = server_addr; (*self)->client_addr = client_addr; (*self)->init_delay_ms = init_delay_ms; (*self)->max_delay_ms = max_delay_ms; (*self)->connected_cb = connected_cb; (*self)->connected_cb_arg = connected_cb_arg; (*self)->working = 0; (*self)->state = SVX_TCP_CONNECTOR_STATE_DISCONNECTED; (*self)->fd = -1; (*self)->channel = NULL; (*self)->cur_delay_ms = init_delay_ms; SVX_LOOPER_TIMER_ID_INIT(&((*self)->retry_timer_id)); return 0; }
int svx_poller_destroy(svx_poller_t **self) { if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); if(NULL == *self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "*self:%p\n", *self); (*self)->handlers->destroy(&((*self)->obj)); free(*self); *self = NULL; return 0; }
int svx_circlebuf_destroy(svx_circlebuf_t **self) { if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); if(NULL == *self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "*self:%p\n", *self); if((*self)->buf) free((*self)->buf); free(*self); *self = NULL; return 0; }
int svx_crash_set_dirname(const char *dirname) { int r = 0; if(NULL == dirname || '\0' == dirname[0] || '/' != dirname[strlen(dirname) - 1]) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "dirname:%s\n", dirname); if(0 != (r = svx_util_get_absolute_path(dirname, svx_crash_dirname, sizeof(svx_crash_dirname)))) SVX_LOG_ERRNO_RETURN_ERR(r, NULL); return 0; }
int svx_inetaddr_from_ipport(svx_inetaddr_t *self, const char *ip, uint16_t port) { int r = 0; char *ifname = NULL; char ipv6[INET6_ADDRSTRLEN + 1 + IF_NAMESIZE + 1]; if(NULL == self || NULL == ip) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, ip:%p\n", self, ip); memset(self, 0, sizeof(svx_inetaddr_t)); if(!strchr(ip, ':')) { /* IPv4 */ self->storage.addr4.sin_family = AF_INET; self->storage.addr4.sin_port = htons(port); if(1 != (r = inet_pton(AF_INET, ip, &(self->storage.addr4.sin_addr)))) SVX_LOG_ERRNO_RETURN_ERR((0 == r ? SVX_ERRNO_INVAL : errno), "ip:%s\n", ip); } else { /* IPv6 */ self->storage.addr6.sin6_family = AF_INET6; self->storage.addr6.sin6_port = htons(port); self->storage.addr6.sin6_flowinfo = 0; /* ignored, always 0 */ self->storage.addr6.sin6_scope_id = 0; /* only used for link-local IPv6 address */ strncpy(ipv6, ip, sizeof(ipv6)); if(NULL != (ifname = strchr(ipv6, '%'))) { *ifname = '\0'; ifname++; } if(1 != (r = inet_pton(AF_INET6, ipv6, &(self->storage.addr6.sin6_addr)))) SVX_LOG_ERRNO_RETURN_ERR((0 == r ? SVX_ERRNO_INVAL : errno), "ip:%s\n", ipv6); /* set scope id for link-local IPv6 address */ /* man ipv6(7): Linux supports it only for link-local addresses, in that case sin6_scope_id contains the interface index */ if(IN6_IS_ADDR_LINKLOCAL(&(self->storage.addr6.sin6_addr)) || IN6_IS_ADDR_MC_LINKLOCAL(&(self->storage.addr6.sin6_addr))) { if(NULL == ifname || '\0' == *ifname) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_FORMAT, "IPv6 link-local address without interface: %s\n", ip); if(0 == (self->storage.addr6.sin6_scope_id = if_nametoindex(ifname))) SVX_LOG_ERRNO_RETURN_ERR(errno, "ifname:%s\n", ifname); } } return 0; }
int svx_circlebuf_get_data(svx_circlebuf_t *self, uint8_t *buf, size_t buf_len) { if(NULL == self || NULL == buf || 0 == buf_len) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, buf:%p, buf_len:%zu\n", self, buf, buf_len); if(buf_len > self->used) return SVX_ERRNO_NODATA; SVX_CIRCLEBUF_DEBUG("before"); if(self->offset_r < self->offset_w) { /* r w ---xxx--- 012345678 */ memcpy(buf, self->buf + self->offset_r, buf_len); self->used -= buf_len; self->offset_r += buf_len; } else if(self->offset_w < self->offset_r || (self->offset_w == self->offset_r && self->used > 0)) { /* r w r w xxx---xxx OR xxxxxxxxx 012345678 012345678 */ if(buf_len <= self->size - self->offset_r) { memcpy(buf, self->buf + self->offset_r, buf_len); } else { memcpy(buf, self->buf + self->offset_r, self->size - self->offset_r); memcpy(buf + (self->size - self->offset_r), self->buf, buf_len - (self->size - self->offset_r)); } self->used -= buf_len; self->offset_r += buf_len; self->offset_r %= self->size; } else /* self->offset_w == self->offset_r && 0 == self->used */ { /* r w --------- 012345678 */ /* impossible */ SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NODATA, NULL); } SVX_CIRCLEBUF_DEBUG("after"); return 0; }
int svx_inetaddr_get_addr_str(svx_inetaddr_t *self, char *addr, size_t len) { char ip[SVX_INETADDR_STR_IP_LEN] = ""; uint16_t port = 0; int r = 0; if(NULL == self || NULL == addr || len < SVX_INETADDR_STR_ADDR_LEN) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, addr:%p, len:%zu\n", self, addr, len); if(0 != (r = svx_inetaddr_get_ipport(self, ip, sizeof(ip), &port))) SVX_LOG_ERRNO_RETURN_ERR(r, NULL); snprintf(addr, len, "[%s]:%"PRIu16, ip, port); return 0; }
int svx_circlebuf_commit_data(svx_circlebuf_t *self, size_t len) { if(NULL == self || 0 == len) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, len:%zu\n", self, len); if(len > self->size - self->used) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_RANGE, "len:%zu, self->size:%zu, self->used:%zu\n", len, self->size, self->used); SVX_CIRCLEBUF_DEBUG("before"); self->used += len; self->offset_w += len; self->offset_w %= self->size; SVX_CIRCLEBUF_DEBUG("after"); return 0; }
int svx_inetaddr_from_addr(svx_inetaddr_t *self, const struct sockaddr *addr) { socklen_t addrlen = 0; if(NULL == self || NULL == addr) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, addr:%p\n", self, addr); if(AF_INET != addr->sa_family && AF_INET6 != addr->sa_family) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "addr->sa_family:%hu\n", addr->sa_family); addrlen = (AF_INET == addr->sa_family ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); memset(self, 0, sizeof(svx_inetaddr_t)); memcpy(&(self->storage.addr), addr, addrlen); return 0; }
int svx_tcp_connector_connect(svx_tcp_connector_t *self) { int r = 0; if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); if(1 == self->working) SVX_LOG_ERRNO_RETURN_WARNING(SVX_ERRNO_INPROGRESS, NULL); svx_tcp_connector_reset(self); self->working = 1; if(0 != (r = svx_tcp_connector_try(self))) SVX_LOG_ERRNO_RETURN_ERR(r, NULL); return 0; }
int svx_tcp_connector_destroy(svx_tcp_connector_t **self) { if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); if(NULL == *self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "*self:%p\n", *self); if(!SVX_LOOPER_TIMER_ID_IS_INITIALIZER(&((*self)->retry_timer_id))) svx_looper_cancel((*self)->looper, (*self)->retry_timer_id); svx_tcp_connector_reset(*self); free(*self); *self = NULL; return 0; }
int svx_inetaddr_from_fd_local(svx_inetaddr_t *self, int fd) { socklen_t addrlen = 0; if(NULL == self || fd < 0) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, fd:%d\n", self, fd); addrlen = sizeof(self->storage); if(0 != getsockname(fd, &(self->storage.addr), &addrlen)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); if(!((AF_INET == self->storage.addr.sa_family && sizeof(struct sockaddr_in) == addrlen) || (AF_INET6 == self->storage.addr.sa_family && sizeof(struct sockaddr_in6) == addrlen))) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOTSPT, "fd:%d, family:%u, addrlen:%u\n", fd, self->storage.addr.sa_family, addrlen); return 0; }
int svx_poller_update_channel(svx_poller_t *self, svx_channel_t *channel) { if(NULL == self || NULL == channel) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, channel:%p\n", self, channel); return self->handlers->update_channel(self->obj, channel); }
int svx_circlebuf_get_freespace_len(svx_circlebuf_t *self, size_t *len) { if(NULL == self || NULL == len) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, len:%p\n", self, len); *len = self->size - self->used; return 0; }
int svx_tcp_connector_set_client_addr(svx_tcp_connector_t *self, svx_inetaddr_t *client_addr) { if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); self->client_addr = client_addr; return 0; }
int svx_poller_poll(svx_poller_t *self, svx_channel_t **active_channels, size_t active_channels_size, size_t *active_channels_used, int timeout_ms) { if(NULL == self || NULL == active_channels || 0 == active_channels_size || NULL == active_channels_used || timeout_ms < -1) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, active_channels:%p, active_channels_size:%zu, active_channels_used:%p, timeout_ms:%d\n", self, active_channels, active_channels_size, active_channels_used, timeout_ms); return self->handlers->poll(self->obj, active_channels, active_channels_size, active_channels_used, timeout_ms); }
int svx_crash_set_head_msg(const char *msg) { if(NULL == msg) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "msg:%p\n", msg); strncpy(svx_crash_head_msg, msg, sizeof(svx_crash_head_msg)); svx_crash_head_msg[sizeof(svx_crash_head_msg) - 1] = '\0'; return 0; }
int svx_tcp_connector_cancel(svx_tcp_connector_t *self) { if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); if(0 == self->working) SVX_LOG_ERRNO_RETURN_WARNING(SVX_ERRNO_NOTRUN, NULL); svx_looper_cancel(self->looper, self->retry_timer_id); svx_tcp_connector_reset(self); return 0; }
int svx_crash_set_callback(svx_crash_callback_t cb, void *arg) { if(NULL == cb) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "cb:%p\n", cb); svx_crash_cb = cb; svx_crash_cb_arg = arg; return 0; }
int svx_poller_create(svx_poller_t **self) { int r = 0; const svx_poller_handlers_t *handlers = NULL; if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); /* choose a poller */ switch(svx_poller_fixed) { case SVX_POLLER_FIXED_EPOLL: #if SVX_HAVE_EPOLL handlers = &svx_poller_epoll_handlers; #else SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "You fixed poller to EPOLL, but the system does not support it.\n"); #endif break; case SVX_POLLER_FIXED_POLL: handlers = &svx_poller_poll_handlers; break; case SVX_POLLER_FIXED_SELECT: handlers = &svx_poller_select_handlers; break; case SVX_POLLER_FIXED_NONE: default: handlers = svx_poller_handlers_array[0]; break; } if(NULL == (*self = malloc(sizeof(svx_poller_t)))) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOMEM, NULL); (*self)->obj = NULL; (*self)->handlers = handlers; if(0 != (r = (*self)->handlers->create(&((*self)->obj)))) SVX_LOG_ERRNO_GOTO_ERR(err, r, NULL); return 0; err: if(*self) { free(*self); *self = NULL; } return r; }
int svx_crash_set_suffix(const char *suffix) { if(NULL == suffix || '\0' == suffix[0] || strchr(suffix, '/')) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "suffix:%s\n", suffix); strncpy(svx_crash_suffix, suffix, sizeof(svx_crash_suffix)); svx_crash_suffix[sizeof(svx_crash_suffix) - 1] = '\0'; return 0; }
int svx_circlebuf_erase_all_data(svx_circlebuf_t *self) { if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); self->used = 0; self->offset_r = 0; self->offset_w = 0; return 0; }
int svx_tcp_connector_set_retry_delay_ms(svx_tcp_connector_t *self, int64_t init_delay_ms, int64_t max_delay_ms) { if(NULL == self || init_delay_ms < 0 || max_delay_ms < 0 || init_delay_ms > max_delay_ms) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, init_delay_ms:%"PRId64", max_delay_ms:%"PRId64"\n", self, init_delay_ms, max_delay_ms); self->init_delay_ms = init_delay_ms; self->max_delay_ms = max_delay_ms; return 0; }
int svx_inetaddr_get_ipport(svx_inetaddr_t *self, char *ip, size_t ip_len, uint16_t *port) { size_t len; if(NULL == self || ((NULL == ip || ip_len < SVX_INETADDR_STR_IP_LEN) && NULL == port)) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, ip:%p, ip_len:%zu, port:%p\n", self, ip, ip_len, port); switch(self->storage.addr.sa_family) { case AF_INET: if(NULL != ip && ip_len >= SVX_INETADDR_STR_IP_LEN) { memset(ip, 0, ip_len); if(NULL == inet_ntop(AF_INET, &(self->storage.addr4.sin_addr), ip, (socklen_t)ip_len)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); } if(NULL != port) *port = ntohs(self->storage.addr4.sin_port); return 0; case AF_INET6: if(NULL != ip && ip_len >= SVX_INETADDR_STR_IP_LEN) { memset(ip, 0, ip_len); if(NULL == inet_ntop(AF_INET6, &(self->storage.addr6.sin6_addr), ip, (socklen_t)ip_len)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); /* append IPv6 link-local address interface name */ if(IN6_IS_ADDR_LINKLOCAL(&(self->storage.addr6.sin6_addr)) || IN6_IS_ADDR_MC_LINKLOCAL(&(self->storage.addr6.sin6_addr))) { len = strlen(ip); ip[len++] = '%'; if(NULL == if_indextoname(self->storage.addr6.sin6_scope_id, ip + len)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); } } if(NULL != port) *port = ntohs(self->storage.addr6.sin6_port); return 0; default: SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOTSPT, "family:%u\n", self->storage.addr.sa_family); } }
int svx_circlebuf_expand(svx_circlebuf_t *self, size_t freespace_need) { uint8_t *new_buf = NULL; size_t new_size = 0; if(NULL == self) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p\n", self); if(self->size - self->used >= freespace_need) return 0; SVX_CIRCLEBUF_DEBUG("before"); /* check max limit */ if(self->max > 0 && self->used + freespace_need > self->max) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_REACH, "self->max:%zu, self->used:%zu, freespace_need:%zu\n", self->max, self->used, freespace_need); new_size = self->used + freespace_need; if(new_size - self->size < self->step) new_size = self->size + self->step; if(0 != new_size % 8) new_size += (8 - new_size % 8); if(self->max > 0 && new_size > self->max) new_size = self->max; if(NULL == (new_buf = realloc(self->buf, new_size))) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NOMEM, NULL); /* move data if necessary */ if(self->offset_w < self->offset_r || (self->offset_w == self->offset_r && self->used > 0)) { /* r w r w xxx---xxx OR xxxxxxxxx 012345678 012345678 */ memmove(new_buf + (new_size - (self->size - self->offset_r)), new_buf + self->offset_r, self->size - self->offset_r); self->offset_r += (new_size - self->size); } self->buf = new_buf; self->size = new_size; SVX_CIRCLEBUF_DEBUG("after"); return 0; }
int svx_crash_uregister_signal_handler() { /* restore the old sigactions */ if(0 != sigaction(SIGSEGV, &svx_crash_old_sigact_segv, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); if(0 != sigaction(SIGFPE, &svx_crash_old_sigact_fpe, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); if(0 != sigaction(SIGILL, &svx_crash_old_sigact_ill, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); if(0 != sigaction(SIGBUS, &svx_crash_old_sigact_bus, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); if(0 != sigaction(SIGABRT, &svx_crash_old_sigact_abrt, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); #ifdef SIGSTKFLT if(0 != sigaction(SIGSTKFLT, &svx_crash_old_sigact_stkflt, NULL)) SVX_LOG_ERRNO_RETURN_ERR(errno, NULL); #endif return 0; }
static int svx_tcp_connector_retry(svx_tcp_connector_t *self) { int r = 0; /* check if we are still working*/ if(0 == self->working) { svx_tcp_connector_reset(self); return 0; } /* clear */ self->state = SVX_TCP_CONNECTOR_STATE_DISCONNECTED; SVX_LOOPER_TIMER_ID_INIT(&(self->retry_timer_id)); if(self->channel) if(0 != (r = svx_channel_destroy(&(self->channel)))) SVX_LOG_ERRNO_ERR(r, NULL); if(self->fd >= 0) { close(self->fd); self->fd = -1; } /* retry with a delay */ if(0 != (r = svx_looper_run_after(self->looper, svx_tcp_connector_retry_func, NULL, self, self->cur_delay_ms, &(self->retry_timer_id)))) SVX_LOG_ERRNO_RETURN_ERR(r, NULL); /* for next delay */ if(self->cur_delay_ms < self->max_delay_ms) { self->cur_delay_ms *= 2; if(self->cur_delay_ms > self->max_delay_ms) self->cur_delay_ms = self->max_delay_ms; } return 0; }
int svx_circlebuf_get_data_by_ending(svx_circlebuf_t *self, const uint8_t *ending, size_t ending_len, uint8_t *buf, size_t buf_len, size_t *ret_len) { uint8_t *p = NULL; size_t offset_start = 0, offset_end = 0; size_t search_len = 0, search_count = 0; size_t i = 0, j = 0; if(NULL == self || NULL == ending || 0 == ending_len || NULL == buf || 0 == buf_len || NULL == ret_len || ending_len > buf_len) SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_INVAL, "self:%p, ending:%p, ending_len:%zu, buf:%p, buf_len:%zu, " "ret_len:%p\n", self, ending, ending_len, buf, buf_len, ret_len); if(ending_len > self->used) return SVX_ERRNO_NODATA; SVX_CIRCLEBUF_DEBUG("before"); if(self->offset_r < self->offset_w) { /* r w ---xxx--- 012345678 */ if(NULL == (p = memmem(self->buf + self->offset_r, self->used, ending, ending_len))) return SVX_ERRNO_NOTFND; *ret_len = p + ending_len - (self->buf + self->offset_r); if(*ret_len > buf_len) return SVX_ERRNO_NOBUF; memcpy(buf, self->buf + self->offset_r, *ret_len); self->used -= *ret_len; self->offset_r += *ret_len; SVX_CIRCLEBUF_DEBUG("after 1"); return 0; } else if(self->offset_w < self->offset_r || (self->offset_w == self->offset_r && self->used > 0)) { /* r w r w xxx---xxx OR xxxxxxxxx 012345678 012345678 */ /* try to find the ending from self->offset_r to self->size */ if(self->size - self->offset_r >= ending_len) { if(NULL != (p = memmem(self->buf + self->offset_r, self->size - self->offset_r, ending, ending_len))) { *ret_len = p + ending_len - (self->buf + self->offset_r); if(*ret_len > buf_len) return SVX_ERRNO_NOBUF; memcpy(buf, self->buf + self->offset_r, *ret_len); self->used -= *ret_len; self->offset_r += *ret_len; self->offset_r %= self->size; SVX_CIRCLEBUF_DEBUG("after 2"); return 0; } } /* try to find the ending around the "turning point" */ if(self->offset_w > 0 && ending_len > 1) { offset_start = self->size - ending_len + 1; if(offset_start < self->offset_r) offset_start = self->offset_r; offset_end = ending_len - 1; if(offset_end > self->offset_w) offset_end = self->offset_w; search_len = self->size - offset_start + offset_end; search_count = search_len - ending_len + 1; for(i = 0; i < search_count; i++) { for(j = offset_start + i; j < self->size; j++) if(*(self->buf + j) != *(ending + (j - (offset_start + i)))) goto next_round; for(j = 0; j < ending_len - (self->size - (offset_start + i)); j++) if(*(self->buf + j) != *(ending + (self->size - (offset_start + i)) + j)) goto next_round; *ret_len = (self->size - self->offset_r) + j; if(*ret_len > buf_len) return SVX_ERRNO_NOBUF; memcpy(buf, self->buf + self->offset_r, self->size - self->offset_r); memcpy(buf + (self->size - self->offset_r), self->buf, j); self->used -= *ret_len; self->offset_r += *ret_len; self->offset_r %= self->size; SVX_CIRCLEBUF_DEBUG("after 3"); return 0; next_round: continue; } } /* try to find the ending from self->buf to self->offset_w */ if(self->offset_w >= ending_len) { if(NULL != (p = memmem(self->buf, self->offset_w, ending, ending_len))) { *ret_len = (self->size - self->offset_r) + (p + ending_len - self->buf); if(*ret_len > buf_len) return SVX_ERRNO_NOBUF; memcpy(buf, self->buf + self->offset_r, self->size - self->offset_r); memcpy(buf + (self->size - self->offset_r), self->buf, p + ending_len - self->buf); self->used -= *ret_len; self->offset_r += *ret_len; self->offset_r %= self->size; SVX_CIRCLEBUF_DEBUG("after 4"); return 0; } } /* not found */ return SVX_ERRNO_NOTFND; } else /* self->offset_w == self->offset_r && 0 == self->used */ { /* r w --------- 012345678 */ /* impossible */ SVX_LOG_ERRNO_RETURN_ERR(SVX_ERRNO_NODATA, NULL); } }