if (mapped == MAP_FAILED) {
event_warn("%s: mmap(%d, %d, %zu) failed",
__func__, fd, 0, (size_t)(offset + length));
- return (-1);
+ } else {
+ seg->mapping = mapped;
+ seg->contents = (char*)mapped+offset_leftover;
+ seg->offset = 0;
+ seg->type = EVBUF_FS_MMAP;
+ goto done;
}
- chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_fd));
- if (chain == NULL) {
- event_warn("%s: out of memory", __func__);
- munmap(mapped, length);
- return (-1);
+ }
+#endif
+#ifdef _WIN32
+ if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
+ long h = (long)_get_osfhandle(fd);
+ HANDLE m;
+ ev_uint64_t total_size = length+offset;
+ if (h == (long)INVALID_HANDLE_VALUE)
+ return NULL;
+ m = CreateFileMapping((HANDLE)h, NULL, PAGE_READONLY,
+ (total_size >> 32), total_size & 0xfffffffful,
+ NULL);
+ if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */
+ seg->mapping_handle = m;
+ seg->offset = offset;
+ seg->type = EVBUF_FS_MMAP;
+ goto done;
+ }
+ }
+#endif
-
+ {
+ ev_off_t start_pos = lseek(fd, 0, SEEK_CUR), pos;
+ ev_off_t read_so_far = 0;
+ char *mem;
+ int e;
+ ev_ssize_t n = 0;
+ if (!(mem = mm_malloc(length)))
+ goto err;
+ if (start_pos < 0) {
+ mm_free(mem);
+ goto err;
+ }
+ if (lseek(fd, offset, SEEK_SET) < 0) {
+ mm_free(mem);
+ goto err;
+ }
+ while (read_so_far < length) {
+ n = read(fd, mem+read_so_far, length-read_so_far);
+ if (n <= 0)
+ break;
+ read_so_far += n;
}
- chain->flags |= EVBUFFER_MMAP | EVBUFFER_IMMUTABLE;
- chain->buffer = mapped;
- chain->buffer_len = length + offset;
- chain->off = length + offset;
-
- info = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_fd, chain);
- info->fd = fd;
+ e = errno;
+ pos = lseek(fd, start_pos, SEEK_SET);
+ if (n < 0 || (n == 0 && length > read_so_far)) {
+ mm_free(mem);
+ errno = e;
+ goto err;
+ } else if (pos < 0) {
+ mm_free(mem);
+ goto err;
+ }
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- info->fd = -1;
- evbuffer_chain_free(chain);
- ok = 0;
- } else {
- outbuf->n_add_for_cb += length;
+ seg->contents = mem;
+ seg->type = EVBUF_FS_IO;
+ }
- evbuffer_chain_insert(outbuf, chain);
+done:
+ if (!(flags & EVBUF_FS_DISABLE_LOCKING)) {
+ EVTHREAD_ALLOC_LOCK(seg->lock, 0);
+ }
+ return seg;
+err:
+ mm_free(seg);
+ return NULL;
+}
- /* we need to subtract whatever we don't need */
- evbuffer_drain(outbuf, offset);
- }
- } else
+void
+evbuffer_file_segment_free(struct evbuffer_file_segment *seg)
+{
+ int refcnt;
+ EVLOCK_LOCK(seg->lock, 0);
+ refcnt = --seg->refcnt;
+ EVLOCK_UNLOCK(seg->lock, 0);
+ if (refcnt > 0)
+ return;
+ EVUTIL_ASSERT(refcnt == 0);
+
+ if (seg->type == EVBUF_FS_SENDFILE) {
+ ;
+ } else if (seg->type == EVBUF_FS_MMAP) {
+#ifdef _WIN32
+ CloseHandle(seg->mapping_handle);
+#elif defined (_EVENT_HAVE_MMAP)
+ if (munmap(seg->mapping, seg->length) == -1)
+ event_warn("%s: munmap failed", __func__);
#endif
- {
- /* the default implementation */
- struct evbuffer *tmp = evbuffer_new();
- ev_ssize_t read;
+ } else {
+ EVUTIL_ASSERT(seg->type == EVBUF_FS_IO);
+ mm_free(seg->contents);
+ }
- if (tmp == NULL)
- return (-1);
+ if ((seg->flags & EVBUF_FS_CLOSE_ON_FREE) && seg->fd >= 0) {
+ close(seg->fd);
+ }
-#ifdef WIN32
-#define lseek _lseeki64
-#endif
- if (lseek(fd, offset, SEEK_SET) == -1) {
- evbuffer_free(tmp);
- return (-1);
- }
+ EVTHREAD_FREE_LOCK(seg->lock, 0);
+ mm_free(seg);
+}
- /* we add everything to a temporary buffer, so that we
- * can abort without side effects if the read fails.
- */
- while (length) {
- read = evbuffer_readfile(tmp, fd, (ev_ssize_t)length);
- if (read == -1) {
- evbuffer_free(tmp);
- return (-1);
- }
+int
+evbuffer_add_file_segment(struct evbuffer *buf,
+ struct evbuffer_file_segment *seg, ev_off_t offset, ev_off_t length)
+{
+ struct evbuffer_chain *chain;
+ struct evbuffer_chain_file_segment *extra;
- length -= read;
- }
+ EVLOCK_LOCK(seg->lock, 0);
+ ++seg->refcnt;
+ EVLOCK_UNLOCK(seg->lock, 0);
- EVBUFFER_LOCK(outbuf);
- if (outbuf->freeze_end) {
- evbuffer_free(tmp);
- ok = 0;
- } else {
- evbuffer_add_buffer(outbuf, tmp);
- evbuffer_free(tmp);
+ EVBUFFER_LOCK(buf);
-#ifdef WIN32
-#define close _close
-#endif
- close(fd);
+ if (buf->freeze_end)
+ goto err;
+
+ if (length < 0) {
+ if (offset > seg->length)
+ goto err;
+ length = seg->length - offset;
+ }
+
+ /* Can we actually add this? */
+ if (offset+length > seg->length)
+ goto err;
+
+ chain = evbuffer_chain_new(sizeof(struct evbuffer_chain_file_segment));
+ if (!chain)
+ goto err;
+ extra = EVBUFFER_CHAIN_EXTRA(struct evbuffer_chain_file_segment, chain);
+
+ chain->flags |= EVBUFFER_IMMUTABLE|EVBUFFER_FILESEGMENT;
+ if (seg->type == EVBUF_FS_SENDFILE) {
+ chain->flags |= EVBUFFER_SENDFILE;
+ chain->misalign = seg->offset + offset;
+ chain->off = length;
+ chain->buffer_len = chain->misalign + length;
+ } else if (seg->type == EVBUF_FS_MMAP) {
+#ifdef _WIN32
+ ev_uint64_t total_offset = seg->offset+offset;
+ ev_uint64_t offset_rounded=0, offset_remaining=0;
+ LPVOID data;
+ if (total_offset) {
+ SYSTEM_INFO si;
+ memset(&si, 0, sizeof(si)); /* cargo cult */
+ GetSystemInfo(&si);
+ offset_remaining = total_offset % si.dwAllocationGranularity;
+ offset_rounded = total_offset - offset_remaining;
+ }
+ data = MapViewOfFile(
+ seg->mapping_handle,
+ FILE_MAP_READ,
+ offset_rounded >> 32,
+ offset_rounded & 0xfffffffful,
+ length + offset_remaining);
+ if (data == NULL) {
+ mm_free(chain);
+ goto err;
}
+ chain->buffer = (unsigned char*) data;
+ chain->buffer_len = length+offset_remaining;
+ chain->misalign = offset_remaining;
+ chain->off = length;
+#else
+ chain->buffer = (unsigned char*)(seg->contents + offset);
+ chain->buffer_len = length;
+ chain->off = length;
+#endif
+ } else {
+ EVUTIL_ASSERT(seg->type == EVBUF_FS_IO);
+ chain->buffer = (unsigned char*)(seg->contents + offset);
+ chain->buffer_len = length;
+ chain->off = length;
}
- if (ok)
- evbuffer_invoke_callbacks(outbuf);
- EVBUFFER_UNLOCK(outbuf);
+ extra->segment = seg;
+ buf->n_add_for_cb += length;
+ evbuffer_chain_insert(buf, chain);
+
+ evbuffer_invoke_callbacks(buf);
+
+ EVBUFFER_UNLOCK(buf);
- return ok ? 0 : -1;
+ return 0;
+err:
+ EVBUFFER_UNLOCK(buf);
+ evbuffer_file_segment_free(seg);
+ return -1;
}
+int
+evbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length)
+{
+ struct evbuffer_file_segment *seg;
+ unsigned flags = EVBUF_FS_CLOSE_ON_FREE;
+ int r;
+
+ seg = evbuffer_file_segment_new(fd, offset, length, flags);
+ if (!seg)
+ return -1;
+ r = evbuffer_add_file_segment(buf, seg, 0, length);
+ evbuffer_file_segment_free(seg);
+ return r;
+}
void
evbuffer_setcb(struct evbuffer *buffer, evbuffer_cb cb, void *cbarg)
TT_DIE(("Didn't recognize the implementation"));
}
+ if (use_bigfile) {
+ unsigned int i;
+ datalen = 1024*512;
+ data = malloc(1024*512);
+ tt_assert(data);
+ for (i = 0; i < datalen; ++i)
+ data[i] = _evutil_weakrand();
+ } else {
+ data = strdup("here is a relatively small string.");
+ tt_assert(data);
+ datalen = strlen(data);
+ }
+
+ fd = regress_make_tmpfile(data, datalen, &tmpfilename);
+
+ if (map_from_offset) {
+ starting_offset = datalen/4 + 1;
+ mapping_len = datalen / 2 - 1;
+ expect_data = data + starting_offset;
+ expect_len = mapping_len;
+ } else {
+ expect_data = data;
+ expect_len = datalen;
+ }
+ if (view_from_offset) {
+ tt_assert(use_segment); /* Can't do this with add_file*/
+ segment_offset = expect_len / 3;
+ segment_len = expect_len / 2;
+ expect_data = expect_data + segment_offset;
+ expect_len = segment_len;
+ }
+
+ if (use_segment) {
+ seg = evbuffer_file_segment_new(fd, starting_offset,
+ mapping_len, flags);
+ tt_assert(seg);
+ if ((int)seg->type != (int)want_type)
+ tt_skip();
+ }
+
+ /* Say that it drains to a fd so that we can use sendfile. */
+ evbuffer_set_flags(src, EVBUFFER_FLAG_DRAINS_TO_FD);
+
#if defined(_EVENT_HAVE_SENDFILE) && defined(__sun__) && defined(__svr4__)
/* We need to use a pair of AF_INET sockets, since Solaris
doesn't support sendfile() over AF_UNIX. */