File: inhale.c

package info (click to toggle)
libhsync 0.5.7-1.2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 1,060 kB
  • ctags: 543
  • sloc: sh: 7,944; ansic: 5,413; makefile: 154
file content (210 lines) | stat: -rw-r--r-- 6,550 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*=                                     -*- c-file-style: "bsd" -*-
 * rproxy -- dynamic caching and delta update in HTTP
 * $Id: inhale.c,v 1.16 2000/08/07 08:37:17 mbp Exp $
 *
 * Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

                                        /*
                                         | I think if you've ordered
                                         | somebody to do something
                                         | you should probably resist
                                         | the urge to thank them.
                                         |  -- abc.net.au/thegames/
                                         */

/*
 * TODO: Inhale from a mapptr, rather than doing hard IO because bad
 * things happen if it stops short.
 */

#include "includes.h"
#include "command.h"
#include "protocol.h"
#include "prototab.h"
#include "mapptr.h"
#include "inhale.h"

#ifndef __LCLINT__
/* On Linux/glibc this file contains constructs that confuse lclint. */
#  include <netinet/in.h>		/* ntohs, etc */
#endif /* __LCLINT__ */

#include "varint.h"

#ifdef HAVE_SYS_ENDIAN_H
#  include <sys/endian.h>
#endif



/* For debugging porpoises, here are some human-readable forms. */
struct hs_op_kind_name const _hs_op_kind_names[] = {
    {"EOF",       op_kind_eof },
    {"COPY",      op_kind_copy },
    {"LITERAL",   op_kind_literal },
    {"SIGNATURE", op_kind_signature },
    {"CHECKSUM",  op_kind_checksum },
    {"INVALID",   op_kind_invalid },
    {NULL,        0 }
};


/*
 * Return a human-readable name for KIND.
 */
char const *
_hs_op_kind_name(hs_op_kind_t kind)
{
    const struct hs_op_kind_name *k;

    for (k = _hs_op_kind_names; k->kind; k++) {
        if (k->kind == kind) {
            return k->name;
        }
    }

    return NULL;
}


/*
 * Extract parameters from a command starting in memory at P,
 * and with format described by ENT.
 */
static void
_hs_parse_command(byte_t const *p,
                  hs_prototab_ent_t const *ent,
                  int *param1, int *param2)
{
    p++;                        /* skip command byte */

    *param1 = _hs_read_varint(p, ent->len_1);
    p += ent->len_1;

    if (ent->len_2) {
        *param2 = _hs_read_varint(p, ent->len_2);
    }
}


/*
 * Try to map LEN bytes.  If we succeed, *P points to the data and
 * LEN is the amount mapped.
 *
 * If there is not enough data yet, or if we hit EOF, or if something
 * breaks, then return HS_FAILED or HS_AGAIN
 */
static hs_result_t
_hs_inhale_map_cmd(hs_map_t *map, off_t input_pos, byte_t const **p,
                   size_t *len)
{
    size_t require_len;
    int reached_eof;

    require_len = *len;
    *p = hs_map_ptr(map, input_pos, len, &reached_eof);

    if (!*p) {
        _hs_error("couldn't map command byte");
        return HS_FAILED;
    } else if (*len < require_len && reached_eof) {
        /* This is a warning condition, because we shouldn't just run
         * off the end of the file; instead we should get an EOF
         * command and stop smoothly. */
        _hs_error("reached eof when trying to read command byte");
        return HS_FAILED;
    } else if (*len < require_len) {
        /* Perhaps we just couldn't get enough data this time? */
        _hs_trace("only mapped %d bytes towards a command header, require %d",
                  *len, require_len);
        return HS_AGAIN;
    }

    return HS_DONE;
}


/*
 * Read a command from MAP, containing a token sequence.  The input
 * cursor is currently at *INPUT_POS, which is updated to reflect the
 * amount of data read.
 *
 * KIND identifies the kind of command, and if applicable LEN and OFF
 * describe the parameters to the command.
 *
 * If the input routine indicates that it would block, then we return
 * without updating the file cursor.  Then when we come back later, we
 * can try and map at the same position.  We know that that data will
 * still be available, so we can re-read the whole command.
 * Rescanning it is slightly redundant, but easier than worrying about
 * finding a place to explicitly store the state.
 *
 * We first try to map at least one byte, being the command byte.
 * This tells us how many bytes will be required for the command and
 * its parameters, so if necessary we then try to map that many bytes.
 * Then we have the whole command and can interpret it.
 *
 * Returns HS_DONE, HS_AGAIN or HS_FAILED.
 */
hs_result_t
_hs_inhale_command_map(hs_map_t *map, off_t *input_pos,
                       hs_op_kind_t *kind,
                       int *param1, int *param2)
{
    const byte_t *cmd;
    hs_result_t result;
    const hs_prototab_ent_t *ent;
    size_t len;

    /* First, map at least one byte to find the command type. */
    len = 1;
    result = _hs_inhale_map_cmd(map, *input_pos, &cmd, &len);
    if (result != HS_DONE)
        return result;

    /* Now find out what this command means */
    ent = &_hs_prototab[*cmd];
    *kind = ent->kind;

    _hs_trace("inhaled initial byte %#04x, kind=%s, total length will be %d",
              *cmd, _hs_op_kind_name(*kind), ent->total_size);

    if (ent->total_size == 1) {
        /* this is an immediate-parameter command byte: really easy */
        *param1 = ent->immediate;
        *input_pos += 1;
        return HS_DONE;
    }

    if (len < ent->total_size) {
        /* read in enough input data to cover all the parameters */
        len = ent->total_size;
        result = _hs_inhale_map_cmd(map, *input_pos, &cmd, &len);
        if (result != HS_DONE)
            return result;
    }

    /* otherwise, we have to make sure we map the whole command header
     * now that we know the length */
    _hs_parse_command(cmd, ent, param1, param2);
    *input_pos += ent->total_size;

    /* Now we know we have at least one command byte */
    return HS_DONE;
}