1/* id.c : operations on node-revision IDs 2 * 3 * ==================================================================== 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 * ==================================================================== 21 */ 22 23#include <string.h> 24#include <stdlib.h> 25 26#include "id.h" 27#include "../libsvn_fs/fs-loader.h" 28#include "private/svn_temp_serializer.h" 29#include "private/svn_string_private.h" 30 31 32typedef struct id_private_t { 33 const char *node_id; 34 const char *copy_id; 35 const char *txn_id; 36 svn_revnum_t rev; 37 apr_off_t offset; 38} id_private_t; 39 40 41/* Accessing ID Pieces. */ 42 43const char * 44svn_fs_fs__id_node_id(const svn_fs_id_t *id) 45{ 46 id_private_t *pvt = id->fsap_data; 47 48 return pvt->node_id; 49} 50 51 52const char * 53svn_fs_fs__id_copy_id(const svn_fs_id_t *id) 54{ 55 id_private_t *pvt = id->fsap_data; 56 57 return pvt->copy_id; 58} 59 60 61const char * 62svn_fs_fs__id_txn_id(const svn_fs_id_t *id) 63{ 64 id_private_t *pvt = id->fsap_data; 65 66 return pvt->txn_id; 67} 68 69 70svn_revnum_t 71svn_fs_fs__id_rev(const svn_fs_id_t *id) 72{ 73 id_private_t *pvt = id->fsap_data; 74 75 return pvt->rev; 76} 77 78 79apr_off_t 80svn_fs_fs__id_offset(const svn_fs_id_t *id) 81{ 82 id_private_t *pvt = id->fsap_data; 83 84 return pvt->offset; 85} 86 87 88svn_string_t * 89svn_fs_fs__id_unparse(const svn_fs_id_t *id, 90 apr_pool_t *pool) 91{ 92 id_private_t *pvt = id->fsap_data; 93 94 if ((! pvt->txn_id)) 95 { 96 char rev_string[SVN_INT64_BUFFER_SIZE]; 97 char offset_string[SVN_INT64_BUFFER_SIZE]; 98 99 svn__i64toa(rev_string, pvt->rev); 100 svn__i64toa(offset_string, pvt->offset); 101 return svn_string_createf(pool, "%s.%s.r%s/%s", 102 pvt->node_id, pvt->copy_id, 103 rev_string, offset_string); 104 } 105 else 106 { 107 return svn_string_createf(pool, "%s.%s.t%s", 108 pvt->node_id, pvt->copy_id, 109 pvt->txn_id); 110 } 111} 112 113 114/*** Comparing node IDs ***/ 115 116svn_boolean_t 117svn_fs_fs__id_eq(const svn_fs_id_t *a, 118 const svn_fs_id_t *b) 119{ 120 id_private_t *pvta = a->fsap_data, *pvtb = b->fsap_data; 121 122 if (a == b) 123 return TRUE; 124 if (strcmp(pvta->node_id, pvtb->node_id) != 0) 125 return FALSE; 126 if (strcmp(pvta->copy_id, pvtb->copy_id) != 0) 127 return FALSE; 128 if ((pvta->txn_id == NULL) != (pvtb->txn_id == NULL)) 129 return FALSE; 130 if (pvta->txn_id && pvtb->txn_id && strcmp(pvta->txn_id, pvtb->txn_id) != 0) 131 return FALSE; 132 if (pvta->rev != pvtb->rev) 133 return FALSE; 134 if (pvta->offset != pvtb->offset) 135 return FALSE; 136 return TRUE; 137} 138 139 140svn_boolean_t 141svn_fs_fs__id_check_related(const svn_fs_id_t *a, 142 const svn_fs_id_t *b) 143{ 144 id_private_t *pvta = a->fsap_data, *pvtb = b->fsap_data; 145 146 if (a == b) 147 return TRUE; 148 /* If both node_ids start with _ and they have differing transaction 149 IDs, then it is impossible for them to be related. */ 150 if (pvta->node_id[0] == '_') 151 { 152 if (pvta->txn_id && pvtb->txn_id && 153 (strcmp(pvta->txn_id, pvtb->txn_id) != 0)) 154 return FALSE; 155 } 156 157 return (strcmp(pvta->node_id, pvtb->node_id) == 0); 158} 159 160 161int 162svn_fs_fs__id_compare(const svn_fs_id_t *a, 163 const svn_fs_id_t *b) 164{ 165 if (svn_fs_fs__id_eq(a, b)) 166 return 0; 167 return (svn_fs_fs__id_check_related(a, b) ? 1 : -1); 168} 169 170 171 172/* Creating ID's. */ 173 174static id_vtable_t id_vtable = { 175 svn_fs_fs__id_unparse, 176 svn_fs_fs__id_compare 177}; 178 179 180svn_fs_id_t * 181svn_fs_fs__id_txn_create(const char *node_id, 182 const char *copy_id, 183 const char *txn_id, 184 apr_pool_t *pool) 185{ 186 svn_fs_id_t *id = apr_palloc(pool, sizeof(*id)); 187 id_private_t *pvt = apr_palloc(pool, sizeof(*pvt)); 188 189 pvt->node_id = apr_pstrdup(pool, node_id); 190 pvt->copy_id = apr_pstrdup(pool, copy_id); 191 pvt->txn_id = apr_pstrdup(pool, txn_id); 192 pvt->rev = SVN_INVALID_REVNUM; 193 pvt->offset = -1; 194 195 id->vtable = &id_vtable; 196 id->fsap_data = pvt; 197 return id; 198} 199 200 201svn_fs_id_t * 202svn_fs_fs__id_rev_create(const char *node_id, 203 const char *copy_id, 204 svn_revnum_t rev, 205 apr_off_t offset, 206 apr_pool_t *pool) 207{ 208 svn_fs_id_t *id = apr_palloc(pool, sizeof(*id)); 209 id_private_t *pvt = apr_palloc(pool, sizeof(*pvt)); 210 211 pvt->node_id = apr_pstrdup(pool, node_id); 212 pvt->copy_id = apr_pstrdup(pool, copy_id); 213 pvt->txn_id = NULL; 214 pvt->rev = rev; 215 pvt->offset = offset; 216 217 id->vtable = &id_vtable; 218 id->fsap_data = pvt; 219 return id; 220} 221 222 223svn_fs_id_t * 224svn_fs_fs__id_copy(const svn_fs_id_t *id, apr_pool_t *pool) 225{ 226 svn_fs_id_t *new_id = apr_palloc(pool, sizeof(*new_id)); 227 id_private_t *new_pvt = apr_palloc(pool, sizeof(*new_pvt)); 228 id_private_t *pvt = id->fsap_data; 229 230 new_pvt->node_id = apr_pstrdup(pool, pvt->node_id); 231 new_pvt->copy_id = apr_pstrdup(pool, pvt->copy_id); 232 new_pvt->txn_id = pvt->txn_id ? apr_pstrdup(pool, pvt->txn_id) : NULL; 233 new_pvt->rev = pvt->rev; 234 new_pvt->offset = pvt->offset; 235 236 new_id->vtable = &id_vtable; 237 new_id->fsap_data = new_pvt; 238 return new_id; 239} 240 241 242svn_fs_id_t * 243svn_fs_fs__id_parse(const char *data, 244 apr_size_t len, 245 apr_pool_t *pool) 246{ 247 svn_fs_id_t *id; 248 id_private_t *pvt; 249 char *data_copy, *str; 250 251 /* Dup the ID data into POOL. Our returned ID will have references 252 into this memory. */ 253 data_copy = apr_pstrmemdup(pool, data, len); 254 255 /* Alloc a new svn_fs_id_t structure. */ 256 id = apr_palloc(pool, sizeof(*id)); 257 pvt = apr_palloc(pool, sizeof(*pvt)); 258 id->vtable = &id_vtable; 259 id->fsap_data = pvt; 260 261 /* Now, we basically just need to "split" this data on `.' 262 characters. We will use svn_cstring_tokenize, which will put 263 terminators where each of the '.'s used to be. Then our new 264 id field will reference string locations inside our duplicate 265 string.*/ 266 267 /* Node Id */ 268 str = svn_cstring_tokenize(".", &data_copy); 269 if (str == NULL) 270 return NULL; 271 pvt->node_id = str; 272 273 /* Copy Id */ 274 str = svn_cstring_tokenize(".", &data_copy); 275 if (str == NULL) 276 return NULL; 277 pvt->copy_id = str; 278 279 /* Txn/Rev Id */ 280 str = svn_cstring_tokenize(".", &data_copy); 281 if (str == NULL) 282 return NULL; 283 284 if (str[0] == 'r') 285 { 286 apr_int64_t val; 287 svn_error_t *err; 288 289 /* This is a revision type ID */ 290 pvt->txn_id = NULL; 291 292 data_copy = str + 1; 293 str = svn_cstring_tokenize("/", &data_copy); 294 if (str == NULL) 295 return NULL; 296 pvt->rev = SVN_STR_TO_REV(str); 297 298 str = svn_cstring_tokenize("/", &data_copy); 299 if (str == NULL) 300 return NULL; 301 err = svn_cstring_atoi64(&val, str); 302 if (err) 303 { 304 svn_error_clear(err); 305 return NULL; 306 } 307 pvt->offset = (apr_off_t)val; 308 } 309 else if (str[0] == 't') 310 { 311 /* This is a transaction type ID */ 312 pvt->txn_id = str + 1; 313 pvt->rev = SVN_INVALID_REVNUM; 314 pvt->offset = -1; 315 } 316 else 317 return NULL; 318 319 return id; 320} 321 322/* (de-)serialization support */ 323 324/* Serialization of the PVT sub-structure within the CONTEXT. 325 */ 326static void 327serialize_id_private(svn_temp_serializer__context_t *context, 328 const id_private_t * const *pvt) 329{ 330 const id_private_t *private = *pvt; 331 332 /* serialize the pvt data struct itself */ 333 svn_temp_serializer__push(context, 334 (const void * const *)pvt, 335 sizeof(*private)); 336 337 /* append the referenced strings */ 338 svn_temp_serializer__add_string(context, &private->node_id); 339 svn_temp_serializer__add_string(context, &private->copy_id); 340 svn_temp_serializer__add_string(context, &private->txn_id); 341 342 /* return to caller's nesting level */ 343 svn_temp_serializer__pop(context); 344} 345 346/* Serialize an ID within the serialization CONTEXT. 347 */ 348void 349svn_fs_fs__id_serialize(svn_temp_serializer__context_t *context, 350 const struct svn_fs_id_t * const *id) 351{ 352 /* nothing to do for NULL ids */ 353 if (*id == NULL) 354 return; 355 356 /* serialize the id data struct itself */ 357 svn_temp_serializer__push(context, 358 (const void * const *)id, 359 sizeof(**id)); 360 361 /* serialize the id_private_t data sub-struct */ 362 serialize_id_private(context, 363 (const id_private_t * const *)&(*id)->fsap_data); 364 365 /* return to caller's nesting level */ 366 svn_temp_serializer__pop(context); 367} 368 369/* Deserialization of the PVT sub-structure in BUFFER. 370 */ 371static void 372deserialize_id_private(void *buffer, id_private_t **pvt) 373{ 374 /* fixup the reference to the only sub-structure */ 375 id_private_t *private; 376 svn_temp_deserializer__resolve(buffer, (void**)pvt); 377 378 /* fixup the sub-structure itself */ 379 private = *pvt; 380 svn_temp_deserializer__resolve(private, (void**)&private->node_id); 381 svn_temp_deserializer__resolve(private, (void**)&private->copy_id); 382 svn_temp_deserializer__resolve(private, (void**)&private->txn_id); 383} 384 385/* Deserialize an ID inside the BUFFER. 386 */ 387void 388svn_fs_fs__id_deserialize(void *buffer, svn_fs_id_t **id) 389{ 390 /* The id maybe all what is in the whole buffer. 391 * Don't try to fixup the pointer in that case*/ 392 if (*id != buffer) 393 svn_temp_deserializer__resolve(buffer, (void**)id); 394 395 /* no id, no sub-structure fixup necessary */ 396 if (*id == NULL) 397 return; 398 399 /* the stored vtable is bogus at best -> set the right one */ 400 (*id)->vtable = &id_vtable; 401 402 /* handle sub-structures */ 403 deserialize_id_private(*id, (id_private_t **)&(*id)->fsap_data); 404} 405 406