ultragrep 0.1.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/ultragrep_build_indexes +45 -0
- data/lib/ultragrep.rb +47 -61
- data/lib/ultragrep/config.rb +6 -0
- data/lib/ultragrep/log_collector.rb +67 -0
- data/lib/ultragrep/version.rb +1 -1
- data/src/Makefile +24 -0
- data/{ext/ultragrep → src}/extconf.rb +0 -0
- data/src/pcre.h +668 -0
- data/src/request.h +13 -0
- data/src/ug_build_index.c +109 -0
- data/src/ug_cat.c +188 -0
- data/src/ug_guts.c +199 -0
- data/src/ug_gzip.c +242 -0
- data/src/ug_gzip.h +8 -0
- data/src/ug_index.c +62 -0
- data/src/ug_index.h +23 -0
- data/src/ug_lua.c +119 -0
- data/src/ug_lua.h +10 -0
- metadata +25 -28
- data/ext/ultragrep/Makefile +0 -39
- data/ext/ultragrep/rails_req.c +0 -102
- data/ext/ultragrep/rails_req.h +0 -6
- data/ext/ultragrep/req_matcher.h +0 -17
- data/ext/ultragrep/request.c +0 -41
- data/ext/ultragrep/request.h +0 -22
- data/ext/ultragrep/ug_build_index.c +0 -99
- data/ext/ultragrep/ug_cat.c +0 -46
- data/ext/ultragrep/ug_guts.c +0 -138
- data/ext/ultragrep/ug_index.c +0 -83
- data/ext/ultragrep/ug_index.h +0 -27
- data/ext/ultragrep/work_req.c +0 -200
- data/ext/ultragrep/work_req.h +0 -6
- data/ext/ultragrep/zran.c +0 -291
data/src/ug_gzip.c
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
// ex: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
|
2
|
+
/* zran.c -- example of zlib/gzip stream indexing and random access
|
3
|
+
* Copyright (C) 2005 Mark Adler
|
4
|
+
* For conditions of distribution and use, see copyright notice in zlib.h
|
5
|
+
Version 1.0 29 May 2005 Mark Adler */
|
6
|
+
|
7
|
+
/* for gzipped logs we keep two indexes. One is like this:
|
8
|
+
* [timestamp, uncompressed_offset]
|
9
|
+
* [timestamp, uncompressed_offset]
|
10
|
+
* ...
|
11
|
+
*
|
12
|
+
* And one is like this:
|
13
|
+
* [uncompressed_offset, compressed_offset, gzip_data]
|
14
|
+
* [uncompressed_offset, compressed_offset, gzip_data]
|
15
|
+
*
|
16
|
+
* so we can first get the uncompressed offset and then start extracting in the
|
17
|
+
* file from the correct spot. Mark Adler's comments follow. */
|
18
|
+
|
19
|
+
/* Illustrate the use of Z_BLOCK, inflatePrime(), and inflateSetDictionary()
|
20
|
+
for random access of a compressed file. A file containing a zlib or gzip
|
21
|
+
stream is provided on the command line. The compressed stream is decoded in
|
22
|
+
its entirety, and an index built with access points about every SPAN bytes
|
23
|
+
in the uncompressed output. The compressed file is left open, and can then
|
24
|
+
be read randomly, having to decompress on the average SPAN/2 uncompressed
|
25
|
+
bytes before getting to the desired block of data.
|
26
|
+
|
27
|
+
An access point can be created at the start of any deflate block, by saving
|
28
|
+
the starting file offset and bit of that block, and the 32K bytes of
|
29
|
+
uncompressed data that precede that block. Also the uncompressed offset of
|
30
|
+
that block is saved to provide a referece for locating a desired starting
|
31
|
+
point in the uncompressed stream. build_index() works by decompressing the
|
32
|
+
input zlib or gzip stream a block at a time, and at the end of each block
|
33
|
+
deciding if enough uncompressed data has gone by to justify the creation of
|
34
|
+
a new access point. If so, that point is saved in a data structure that
|
35
|
+
grows as needed to accommodate the points.
|
36
|
+
|
37
|
+
To use the index, an offset in the uncompressed data is provided, for which
|
38
|
+
the latest accees point at or preceding that offset is located in the index.
|
39
|
+
The input file is positioned to the specified location in the index, and if
|
40
|
+
necessary the first few bits of the compressed data is read from the file.
|
41
|
+
inflate is initialized with those bits and the 32K of uncompressed data, and
|
42
|
+
the decompression then proceeds until the desired offset in the file is
|
43
|
+
reached. Then the decompression continues to read the desired uncompressed
|
44
|
+
data from the file.
|
45
|
+
*/
|
46
|
+
|
47
|
+
#include <stdio.h>
|
48
|
+
#include <stdlib.h>
|
49
|
+
#include <string.h>
|
50
|
+
#include "zlib.h"
|
51
|
+
#include "ug_index.h"
|
52
|
+
#include "ug_lua.h"
|
53
|
+
#include "ug_gzip.h"
|
54
|
+
|
55
|
+
|
56
|
+
// how often (in uncompressed bytes) to add an index
|
57
|
+
#define INDEX_EVERY_NBYTES 30000000
|
58
|
+
|
59
|
+
/*
|
60
|
+
* parse the contents of the circular gzip buffer line by line. when a request
|
61
|
+
* spans gzip blocks, we have to leave the request in the buffer and wait for the next block
|
62
|
+
*
|
63
|
+
* maintains 3 pointers - where we would read next in the buffer, where we're writing next, and the
|
64
|
+
* allocated outputbuffer.
|
65
|
+
*/
|
66
|
+
|
67
|
+
struct gz_output_context {
|
68
|
+
unsigned char *window;
|
69
|
+
int window_len;
|
70
|
+
|
71
|
+
unsigned char *start; // point in the window where the data is to be read from
|
72
|
+
|
73
|
+
char *line; // buffer for an output line
|
74
|
+
int line_len;
|
75
|
+
off_t total_out;
|
76
|
+
off_t total_in;
|
77
|
+
off_t last_index_offset;
|
78
|
+
|
79
|
+
build_idx_context_t *build_idx_context;
|
80
|
+
};
|
81
|
+
|
82
|
+
void process_circular_buffer(struct gz_output_context *c)
|
83
|
+
{
|
84
|
+
int eol;
|
85
|
+
unsigned char *p, *end_of_window;
|
86
|
+
|
87
|
+
end_of_window = c->window + c->window_len;
|
88
|
+
for (;;) {
|
89
|
+
p = c->start;
|
90
|
+
|
91
|
+
/* skip to newline or end of buffer */
|
92
|
+
while ((*p != '\n') && p < end_of_window)
|
93
|
+
p++;
|
94
|
+
|
95
|
+
eol = (p < end_of_window);
|
96
|
+
if ( eol ) p++; /* preserve newline */
|
97
|
+
|
98
|
+
c->line = realloc(c->line, (p - c->start) + c->line_len + 1);
|
99
|
+
memcpy(c->line + c->line_len, c->start, p - c->start);
|
100
|
+
c->line_len += (p - c->start);
|
101
|
+
c->total_out += (p - c->start);
|
102
|
+
|
103
|
+
if (!eol) {
|
104
|
+
if (c->window_len == WINSIZE) /* buffer is full, wrap back to top of input buffer to complete the line */
|
105
|
+
c->start = c->window;
|
106
|
+
else /* out of data in the input buffer but the line didn't terminate, continue to next block to complete the line */
|
107
|
+
c->start = p;
|
108
|
+
return;
|
109
|
+
} else {
|
110
|
+
c->line[c->line_len] = '\0';
|
111
|
+
ug_process_line(c->build_idx_context->lua, c->line, c->line_len, c->total_out - c->line_len);
|
112
|
+
|
113
|
+
free(c->line);
|
114
|
+
c->line = NULL;
|
115
|
+
c->line_len = 0;
|
116
|
+
c->start = p;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
int need_gz_index(z_stream * strm, struct gz_output_context *c)
|
122
|
+
{
|
123
|
+
if (!((strm->data_type & 128) && !(strm->data_type & 64)))
|
124
|
+
return 0;
|
125
|
+
return c->last_index_offset == 0 || (c->total_out - c->last_index_offset) > INDEX_EVERY_NBYTES;
|
126
|
+
}
|
127
|
+
|
128
|
+
void add_gz_index(z_stream * strm, struct gz_output_context *c, unsigned char *window)
|
129
|
+
{
|
130
|
+
off_t compressed_offset;
|
131
|
+
|
132
|
+
compressed_offset = (((uint64_t) strm->data_type & 7) << 56);
|
133
|
+
compressed_offset |= (c->total_in & 0x00FFFFFFFFFFFFFF);
|
134
|
+
|
135
|
+
fwrite(&(c->total_out), sizeof(off_t), 1, c->build_idx_context->fgzindex);
|
136
|
+
fwrite(&compressed_offset, sizeof(off_t), 1, c->build_idx_context->fgzindex);
|
137
|
+
|
138
|
+
if (strm->avail_out) {
|
139
|
+
fwrite(window + (WINSIZE - strm->avail_out), strm->avail_out, 1, c->build_idx_context->fgzindex);
|
140
|
+
}
|
141
|
+
|
142
|
+
/* copy from beginning -> middle of buffer if needed */
|
143
|
+
if (strm->avail_out < WINSIZE)
|
144
|
+
fwrite(window, WINSIZE - strm->avail_out, 1, c->build_idx_context->fgzindex);
|
145
|
+
|
146
|
+
c->last_index_offset = c->total_out;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* Make one entire pass through the compressed stream and build an index, with
|
150
|
+
access points about every span bytes of uncompressed output -- span is
|
151
|
+
chosen to balance the speed of random access against the memory requirements
|
152
|
+
of the list, about 32K bytes per access point. Note that data after the end
|
153
|
+
of the first zlib or gzip stream in the file is ignored. build_index()
|
154
|
+
returns the number of access points on success (>= 1), Z_MEM_ERROR for out
|
155
|
+
of memory, Z_DATA_ERROR for an error in the input file, or Z_ERRNO for a
|
156
|
+
file read error. On success, *built points to the resulting index. */
|
157
|
+
|
158
|
+
int build_gz_index(build_idx_context_t * cxt)
|
159
|
+
{
|
160
|
+
int ret;
|
161
|
+
z_stream strm;
|
162
|
+
unsigned char input[CHUNK];
|
163
|
+
unsigned char window[WINSIZE];
|
164
|
+
struct gz_output_context output_cxt;
|
165
|
+
|
166
|
+
bzero(&strm, sizeof(z_stream));
|
167
|
+
bzero(&output_cxt, sizeof(struct gz_output_context));
|
168
|
+
|
169
|
+
output_cxt.window = output_cxt.start = window;
|
170
|
+
output_cxt.build_idx_context = cxt;
|
171
|
+
|
172
|
+
ret = inflateInit2(&strm, 47); /* automatic zlib or gzip decoding */
|
173
|
+
if (ret != Z_OK)
|
174
|
+
return ret;
|
175
|
+
|
176
|
+
/* inflate the input, maintain a sliding window, and build an index -- this
|
177
|
+
also validates the integrity of the compressed data using the check
|
178
|
+
information at the end of the gzip or zlib stream */
|
179
|
+
|
180
|
+
strm.avail_out = 0;
|
181
|
+
do {
|
182
|
+
/* get some compressed data from input file */
|
183
|
+
strm.avail_in = fread(input, 1, CHUNK, cxt->flog);
|
184
|
+
if (ferror(cxt->flog)) {
|
185
|
+
ret = Z_ERRNO;
|
186
|
+
goto build_index_error;
|
187
|
+
}
|
188
|
+
if (strm.avail_in == 0) {
|
189
|
+
ret = Z_DATA_ERROR;
|
190
|
+
goto build_index_error;
|
191
|
+
}
|
192
|
+
strm.next_in = input;
|
193
|
+
|
194
|
+
/* process all of that, or until end of stream */
|
195
|
+
do {
|
196
|
+
/* reset to top of circular buffer */
|
197
|
+
if (strm.avail_out == 0) {
|
198
|
+
strm.avail_out = WINSIZE;
|
199
|
+
strm.next_out = window;
|
200
|
+
}
|
201
|
+
|
202
|
+
/* inflate until out of input, output, or at end of block --
|
203
|
+
update the total input and output counters */
|
204
|
+
output_cxt.total_in += strm.avail_in;
|
205
|
+
ret = inflate(&strm, Z_BLOCK); /* return at end of block */
|
206
|
+
output_cxt.total_in -= strm.avail_in;
|
207
|
+
if (ret == Z_NEED_DICT)
|
208
|
+
ret = Z_DATA_ERROR;
|
209
|
+
if (ret == Z_MEM_ERROR || ret == Z_DATA_ERROR)
|
210
|
+
goto build_index_error;
|
211
|
+
if (ret == Z_STREAM_END)
|
212
|
+
break;
|
213
|
+
|
214
|
+
output_cxt.window_len = WINSIZE - strm.avail_out;
|
215
|
+
|
216
|
+
/* process the uncompressed line data so that the timestamp -> uncompressed offset index gets written */
|
217
|
+
process_circular_buffer(&output_cxt);
|
218
|
+
|
219
|
+
/*
|
220
|
+
*
|
221
|
+
* at the start of a gzip block we need to store the last 32k of data, and a
|
222
|
+
* at the end of a gzip block we reset our context information, so if handle_request
|
223
|
+
* decides to add an index somewhere inside this block we can have an index to the gzip block.
|
224
|
+
*
|
225
|
+
* note that we store the bit offset in the high byte of the offset field in the index.
|
226
|
+
*/
|
227
|
+
if (need_gz_index(&strm, &output_cxt)) {
|
228
|
+
add_gz_index(&strm, &output_cxt, window);
|
229
|
+
}
|
230
|
+
} while (strm.avail_in != 0);
|
231
|
+
} while (ret != Z_STREAM_END);
|
232
|
+
|
233
|
+
/* clean up and return index (release unused entries in list) */
|
234
|
+
(void) inflateEnd(&strm);
|
235
|
+
return 0;
|
236
|
+
|
237
|
+
/* return error */
|
238
|
+
build_index_error:
|
239
|
+
(void) inflateEnd(&strm);
|
240
|
+
return ret;
|
241
|
+
}
|
242
|
+
|
data/src/ug_gzip.h
ADDED
data/src/ug_index.c
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
// ex: set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
|
2
|
+
|
3
|
+
#include <stdlib.h>
|
4
|
+
#include <string.h>
|
5
|
+
#include <libgen.h>
|
6
|
+
#include "ug_index.h"
|
7
|
+
|
8
|
+
void ug_write_index(FILE * file, uint64_t time, uint64_t offset)
|
9
|
+
{
|
10
|
+
fwrite(&time, 8, 1, file);
|
11
|
+
fwrite(&offset, 8, 1, file);
|
12
|
+
}
|
13
|
+
|
14
|
+
int ug_read_index_entry(FILE * file, struct ug_index *idx)
|
15
|
+
{
|
16
|
+
int nread;
|
17
|
+
nread = fread(&(idx->time), 8, 1, file);
|
18
|
+
if (!nread)
|
19
|
+
return 0;
|
20
|
+
|
21
|
+
nread = fread(&(idx->offset), 8, 1, file);
|
22
|
+
return 1;
|
23
|
+
}
|
24
|
+
|
25
|
+
int ug_get_last_index_entry(FILE * file, struct ug_index *idx)
|
26
|
+
{
|
27
|
+
while (ug_read_index_entry(file, idx));
|
28
|
+
return 1;
|
29
|
+
}
|
30
|
+
|
31
|
+
off_t ug_get_offset_for_timestamp(FILE * findex, uint64_t time)
|
32
|
+
{
|
33
|
+
struct ug_index idx;
|
34
|
+
off_t last_offset = 0;
|
35
|
+
|
36
|
+
for (;;) {
|
37
|
+
if (!ug_read_index_entry(findex, &idx))
|
38
|
+
break;
|
39
|
+
|
40
|
+
if (idx.time > time)
|
41
|
+
break;
|
42
|
+
|
43
|
+
last_offset = idx.offset;
|
44
|
+
}
|
45
|
+
|
46
|
+
return last_offset;
|
47
|
+
}
|
48
|
+
|
49
|
+
/* returns malloc'ed memory. */
|
50
|
+
char *ug_get_index_fname(char *log_fname, char *ext)
|
51
|
+
{
|
52
|
+
char *tmp, *dir, *index_fname;
|
53
|
+
|
54
|
+
tmp = strdup(log_fname);
|
55
|
+
dir = dirname(tmp);
|
56
|
+
|
57
|
+
index_fname = malloc(strlen(dir) + strlen(basename(log_fname)) + strlen("/..") + strlen(ext) + 1);
|
58
|
+
|
59
|
+
sprintf(index_fname, "%s/.%s.%s", dir, basename(log_fname), ext);
|
60
|
+
free(tmp);
|
61
|
+
return index_fname;
|
62
|
+
}
|
data/src/ug_index.h
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#include <stdint.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <lua.h>
|
4
|
+
#include <time.h>
|
5
|
+
#define INDEX_EVERY 10
|
6
|
+
|
7
|
+
struct ug_index {
|
8
|
+
uint64_t time;
|
9
|
+
uint64_t offset;
|
10
|
+
};
|
11
|
+
|
12
|
+
typedef struct {
|
13
|
+
time_t last_index_time;
|
14
|
+
FILE *flog;
|
15
|
+
FILE *findex;
|
16
|
+
FILE *fgzindex;
|
17
|
+
lua_State *lua;
|
18
|
+
} build_idx_context_t;
|
19
|
+
|
20
|
+
void ug_write_index(FILE * file, uint64_t time, uint64_t offset);
|
21
|
+
int ug_get_last_index_entry(FILE * file, struct ug_index *idx);
|
22
|
+
off_t ug_get_offset_for_timestamp(FILE * findex, uint64_t time);
|
23
|
+
char *ug_get_index_fname(char *log_fname, char *ext);
|
data/src/ug_lua.c
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
#include <lua.h>
|
2
|
+
#include <lauxlib.h>
|
3
|
+
#include <lualib.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <stdio.h>
|
6
|
+
#include <strings.h>
|
7
|
+
#include "request.h"
|
8
|
+
#include "lua.h"
|
9
|
+
|
10
|
+
int ug_lua_request_add(lua_State *lua);
|
11
|
+
|
12
|
+
static char *strptime_format = NULL;
|
13
|
+
|
14
|
+
lua_State *ug_lua_init(char *fname) {
|
15
|
+
lua_State *lua = luaL_newstate();
|
16
|
+
luaL_openlibs(lua);
|
17
|
+
|
18
|
+
static const struct luaL_Reg ug_request_lib[] = {
|
19
|
+
{"add", ug_lua_request_add},
|
20
|
+
{NULL, NULL}};
|
21
|
+
|
22
|
+
luaL_newlib(lua, ug_request_lib );
|
23
|
+
lua_setglobal(lua, "ug_request");
|
24
|
+
|
25
|
+
if ( luaL_loadfile(lua, fname) != LUA_OK ) {
|
26
|
+
fprintf(stderr, "Couldn't load '%s'\n", fname);
|
27
|
+
fprintf(stderr, "%s\n", lua_tostring(lua, -1));
|
28
|
+
return NULL;
|
29
|
+
}
|
30
|
+
lua_call(lua, 0, 0);
|
31
|
+
|
32
|
+
lua_getglobal(lua, "process_line");
|
33
|
+
if ( lua_isnil(lua, -1) ) {
|
34
|
+
fprintf(stderr, "expected %s to define 'process_line' function\n", fname);
|
35
|
+
return NULL;
|
36
|
+
}
|
37
|
+
lua_pop(lua, 1);
|
38
|
+
|
39
|
+
lua_getglobal(lua, "strptime_format");
|
40
|
+
if ( lua_isnil(lua, -1) ) {
|
41
|
+
fprintf(stderr, "expected %s to define 'strptime_format' string\n", fname);
|
42
|
+
return NULL;
|
43
|
+
}
|
44
|
+
strptime_format = strdup(luaL_checkstring(lua, -1));
|
45
|
+
return lua;
|
46
|
+
}
|
47
|
+
|
48
|
+
#define TM_YEAR_BASE 1900
|
49
|
+
#define EPOCH_YEAR 1970
|
50
|
+
|
51
|
+
|
52
|
+
static time_t
|
53
|
+
sub_mkgmt(struct tm *tm)
|
54
|
+
{
|
55
|
+
int y, nleapdays;
|
56
|
+
time_t t;
|
57
|
+
/* days before the month */
|
58
|
+
static const unsigned short moff[12] = {
|
59
|
+
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
|
60
|
+
};
|
61
|
+
|
62
|
+
/*
|
63
|
+
* XXX: This code assumes the given time to be normalized.
|
64
|
+
* Normalizing here is impossible in case the given time is a leap
|
65
|
+
* second but the local time library is ignorant of leap seconds.
|
66
|
+
*/
|
67
|
+
|
68
|
+
/* minimal sanity checking not to access outside of the array */
|
69
|
+
if ((unsigned) tm->tm_mon >= 12)
|
70
|
+
return (time_t) -1;
|
71
|
+
if (tm->tm_year < EPOCH_YEAR - TM_YEAR_BASE)
|
72
|
+
return (time_t) -1;
|
73
|
+
|
74
|
+
y = tm->tm_year + TM_YEAR_BASE - (tm->tm_mon < 2);
|
75
|
+
nleapdays = y / 4 - y / 100 + y / 400 -
|
76
|
+
((EPOCH_YEAR-1) / 4 - (EPOCH_YEAR-1) / 100 + (EPOCH_YEAR-1) / 400);
|
77
|
+
t = ((((time_t) (tm->tm_year - (EPOCH_YEAR - TM_YEAR_BASE)) * 365 +
|
78
|
+
moff[tm->tm_mon] + tm->tm_mday - 1 + nleapdays) * 24 +
|
79
|
+
tm->tm_hour) * 60 + tm->tm_min) * 60 + tm->tm_sec;
|
80
|
+
|
81
|
+
return (t < 0 ? (time_t) -1 : t);
|
82
|
+
}
|
83
|
+
|
84
|
+
int ug_lua_request_add(lua_State *lua) {
|
85
|
+
struct tm request_tm;
|
86
|
+
const char *timestring;
|
87
|
+
request_t r;
|
88
|
+
|
89
|
+
r.buf = (char *)luaL_checkstring(lua, 1);
|
90
|
+
|
91
|
+
if ( lua_isnil(lua, 2) ) {
|
92
|
+
r.time = 0;
|
93
|
+
} else {
|
94
|
+
timestring = luaL_checkstring(lua, 2);
|
95
|
+
strptime(timestring, strptime_format, &request_tm);
|
96
|
+
r.time = sub_mkgmt(&request_tm);
|
97
|
+
}
|
98
|
+
|
99
|
+
r.offset = luaL_checknumber(lua, 3);
|
100
|
+
handle_request(&r);
|
101
|
+
|
102
|
+
return 1;
|
103
|
+
}
|
104
|
+
|
105
|
+
void ug_process_line(lua_State *lua, char *line, int line_len, off_t offset) {
|
106
|
+
lua_getglobal(lua, "process_line");
|
107
|
+
lua_pushlstring(lua, line, line_len);
|
108
|
+
lua_pushnumber(lua, (lua_Number)offset);
|
109
|
+
lua_call(lua, 2, 0);
|
110
|
+
}
|
111
|
+
|
112
|
+
void ug_lua_on_eof(lua_State *lua) {
|
113
|
+
lua_getglobal(lua, "on_eof");
|
114
|
+
if ( !lua_isnil(lua, -1) ) {
|
115
|
+
lua_call(lua, 0, 0);
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
|