monotone

monotone Mtn Source Tree

Root/transforms.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <iterator>
12#include "botan_pipe_cache.hh"
13#include "botan/botan.h"
14#include "botan/sha160.h"
15#include "gzip.hh"
16
17#include "transforms.hh"
18#include "xdelta.hh"
19#include "char_classifiers.hh"
20
21using std::string;
22using Botan::Pipe;
23using Botan::Base64_Encoder;
24using Botan::Base64_Decoder;
25using Botan::Hex_Encoder;
26using Botan::Hex_Decoder;
27using Botan::Gzip_Compression;
28using Botan::Gzip_Decompression;
29using Botan::Hash_Filter;
30
31// this file contans various sorts of string transformations. each
32// transformation should be self-explanatory from its type signature. see
33// transforms.hh for the summary.
34
35// NB this file uses very "value-centric" functional approach; even though
36// many of the underlying transformations are "stream-centric" and the
37// underlying libraries (eg. crypto++) are stream oriented. this will
38// probably strike some people as contemptably inefficient, since it means
39// that occasionally 1, 2, or even 3 copies of an entire file will wind up
40// in memory at once. I am taking this approach for 3 reasons: first, I
41// want the type system to help me and value types are much easier to work
42// with than stream types. second, it is *much* easier to debug a program
43// that operates on values than streams, and correctness takes precedence
44// over all other features of this program. third, this is a peer-to-peer
45// sort of program for small-ish source-code text files, not a fileserver,
46// and is memory-limited anyways (for example, storing things in sqlite
47// requires they be able to fit in memory). you're hopefully not going to
48// be dealing with hundreds of users hammering on locks and memory
49// concurrently.
50//
51// if future analysis proves these assumptions wrong, feel free to revisit
52// the matter, but bring strong evidence along with you that the stream
53// paradigm "must" be used. this program is intended for source code
54// control and I make no bones about it.
55
56NORETURN(static inline void error_in_transform(Botan::Exception & e));
57
58static inline void
59error_in_transform(Botan::Exception & e)
60{
61 // why do people make up their own out-of-memory exceptions?
62 if (typeid(e) == typeid(Botan::Memory_Exhaustion))
63 throw std::bad_alloc();
64
65 // these classes can all indicate data corruption
66 else if (typeid(e) == typeid(Botan::Encoding_Error)
67 || typeid(e) == typeid(Botan::Decoding_Error)
68 || typeid(e) == typeid(Botan::Stream_IO_Error)
69 || typeid(e) == typeid(Botan::Integrity_Failure))
70 {
71 // clean up the what() string a little: throw away the
72 // "botan: TYPE: " part...
73 string w(e.what());
74 string::size_type pos = w.find(':');
75 pos = w.find(':', pos+1);
76 w = string(w.begin() + pos + 2, w.end());
77
78 // ... downcase the rest of it and replace underscores with spaces.
79 for (string::iterator p = w.begin(); p != w.end(); p++)
80 {
81 *p = to_lower(*p);
82 if (*p == '_')
83 *p = ' ';
84 }
85
86 E(false,
87 F("%s\n"
88 "this may be due to a memory glitch, data corruption during\n"
89 "a network transfer, corruption of your database or workspace,\n"
90 "or a bug in monotone. if the error persists, please contact\n"
91 "%s for assistance.\n")
92 % w % PACKAGE_BUGREPORT);
93 }
94 else
95 throw;
96
97 I(false); // can't get here
98}
99
100// full specializations for the usable cases of xform<XFM>()
101// use extra error checking in base64 and hex decoding
102#define SPECIALIZE_XFORM(T, carg) \
103 template<> string xform<T>(string const & in) \
104 { \
105 string out; \
106 try \
107 { \
108 static cached_botan_pipe pipe(new Pipe(new T(carg))); \
109 /* this might actually be a problem here */ \
110 I(pipe->message_count() < Pipe::LAST_MESSAGE); \
111 pipe->process_msg(in); \
112 out = pipe->read_all_as_string(Pipe::LAST_MESSAGE); \
113 } \
114 catch (Botan::Exception & e) \
115 { \
116 error_in_transform(e); \
117 } \
118 return out; \
119 }
120
121SPECIALIZE_XFORM(Base64_Encoder,);
122SPECIALIZE_XFORM(Base64_Decoder, Botan::IGNORE_WS);
123SPECIALIZE_XFORM(Hex_Encoder, Hex_Encoder::Lowercase);
124SPECIALIZE_XFORM(Hex_Decoder, Botan::IGNORE_WS);
125SPECIALIZE_XFORM(Gzip_Compression,);
126SPECIALIZE_XFORM(Gzip_Decompression,);
127
128template <typename T>
129void pack(T const & in, base64< gzip<T> > & out)
130{
131 string tmp;
132 tmp.reserve(in().size()); // FIXME: do some benchmarking and make this a constant::
133
134 try
135 {
136 static cached_botan_pipe pipe(new Pipe(new Gzip_Compression,
137 new Base64_Encoder));
138 pipe->process_msg(in());
139 tmp = pipe->read_all_as_string(Pipe::LAST_MESSAGE);
140 out = base64< gzip<T> >(tmp);
141 }
142 catch (Botan::Exception & e)
143 {
144 error_in_transform(e);
145 }
146}
147
148template <typename T>
149void unpack(base64< gzip<T> > const & in, T & out)
150{
151 try
152 {
153 static cached_botan_pipe pipe(new Pipe(new Base64_Decoder,
154 new Gzip_Decompression));
155 pipe->process_msg(in());
156 out = T(pipe->read_all_as_string(Pipe::LAST_MESSAGE));
157 }
158 catch (Botan::Exception & e)
159 {
160 error_in_transform(e);
161 }
162}
163
164// specialise them
165template void pack<data>(data const &, base64< gzip<data> > &);
166template void pack<delta>(delta const &, base64< gzip<delta> > &);
167template void unpack<data>(base64< gzip<data> > const &, data &);
168template void unpack<delta>(base64< gzip<delta> > const &, delta &);
169
170
171// identifier (a.k.a. sha1 signature) calculation
172
173void
174calculate_ident(data const & dat,
175 id & ident)
176{
177 try
178 {
179 static cached_botan_pipe p(new Pipe(new Hash_Filter("SHA-160")));
180 p->process_msg(dat());
181 ident = id(p->read_all_as_string(Pipe::LAST_MESSAGE));
182 }
183 catch (Botan::Exception & e)
184 {
185 error_in_transform(e);
186 }
187}
188
189void
190calculate_ident(file_data const & dat,
191 file_id & ident)
192{
193 id tmp;
194 calculate_ident(dat.inner(), tmp);
195 ident = file_id(tmp);
196}
197
198void
199calculate_ident(manifest_data const & dat,
200 manifest_id & ident)
201{
202 id tmp;
203 calculate_ident(dat.inner(), tmp);
204 ident = manifest_id(tmp);
205}
206
207void
208calculate_ident(revision_data const & dat,
209 revision_id & ident)
210{
211 id tmp;
212 calculate_ident(dat.inner(), tmp);
213 ident = revision_id(tmp);
214}
215
216#ifdef BUILD_UNIT_TESTS
217#include "unit_tests.hh"
218#include <stdlib.h>
219
220UNIT_TEST(transform, enc)
221{
222 data d2, d1("the rain in spain");
223 gzip<data> gzd1, gzd2;
224 base64< gzip<data> > bgzd;
225 encode_gzip(d1, gzd1);
226 bgzd = encode_base64(gzd1);
227 gzd2 = decode_base64(bgzd);
228 UNIT_TEST_CHECK(gzd2 == gzd1);
229 decode_gzip(gzd2, d2);
230 UNIT_TEST_CHECK(d2 == d1);
231}
232
233UNIT_TEST(transform, rdiff)
234{
235 data dat1(string("the first day of spring\nmakes me want to sing\n"));
236 data dat2(string("the first day of summer\nis a major bummer\n"));
237 delta del;
238 diff(dat1, dat2, del);
239
240 data dat3;
241 patch(dat1, del, dat3);
242 UNIT_TEST_CHECK(dat3 == dat2);
243}
244
245UNIT_TEST(transform, calculate_ident)
246{
247 data input(string("the only blender which can be turned into the most powerful vaccum cleaner"));
248 id output;
249 string ident("86e03bdb3870e2a207dfd0dcbfd4c4f2e3bc97bd");
250 calculate_ident(input, output);
251 UNIT_TEST_CHECK(output() == decode_hexenc(ident));
252}
253
254UNIT_TEST(transform, corruption_check)
255{
256 data input(string("i'm so fragile, fragile when you're here"));
257 gzip<data> gzd;
258 encode_gzip(input, gzd);
259
260 // fake a single-bit error
261 string gzs = gzd();
262 string::iterator i = gzs.begin();
263 while (*i != '+')
264 i++;
265 *i = 'k';
266
267 gzip<data> gzbad(gzs);
268 data output;
269 UNIT_TEST_CHECK_THROW(decode_gzip(gzbad, output), informative_failure);
270}
271
272#endif // BUILD_UNIT_TESTS
273
274// Local Variables:
275// mode: C++
276// fill-column: 76
277// c-file-style: "gnu"
278// indent-tabs-mode: nil
279// End:
280// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status