1251881Speter/*
2251881Speter * token.c :  routines for doing diffs
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter#include <apr.h>
26251881Speter#include <apr_pools.h>
27251881Speter#include <apr_general.h>
28251881Speter
29251881Speter#include "svn_error.h"
30251881Speter#include "svn_diff.h"
31251881Speter#include "svn_types.h"
32251881Speter
33251881Speter#include "diff.h"
34251881Speter
35251881Speter
36251881Speter/*
37251881Speter * Prime number to use as the size of the hash table.  This number was
38251881Speter * not selected by testing of any kind and may need tweaking.
39251881Speter */
40251881Speter#define SVN_DIFF__HASH_SIZE 127
41251881Speter
42251881Speterstruct svn_diff__node_t
43251881Speter{
44251881Speter  svn_diff__node_t       *parent;
45251881Speter  svn_diff__node_t       *left;
46251881Speter  svn_diff__node_t       *right;
47251881Speter
48251881Speter  apr_uint32_t            hash;
49251881Speter  svn_diff__token_index_t index;
50251881Speter  void                   *token;
51251881Speter};
52251881Speter
53251881Speterstruct svn_diff__tree_t
54251881Speter{
55251881Speter  svn_diff__node_t       *root[SVN_DIFF__HASH_SIZE];
56251881Speter  apr_pool_t             *pool;
57251881Speter  svn_diff__token_index_t node_count;
58251881Speter};
59251881Speter
60251881Speter
61251881Speter/*
62251881Speter * Returns number of tokens in a tree
63251881Speter */
64251881Spetersvn_diff__token_index_t
65251881Spetersvn_diff__get_node_count(svn_diff__tree_t *tree)
66251881Speter{
67251881Speter  return tree->node_count;
68251881Speter}
69251881Speter
70251881Speter/*
71251881Speter * Support functions to build a tree of token positions
72251881Speter */
73251881Speter
74251881Spetervoid
75251881Spetersvn_diff__tree_create(svn_diff__tree_t **tree, apr_pool_t *pool)
76251881Speter{
77251881Speter  *tree = apr_pcalloc(pool, sizeof(**tree));
78251881Speter  (*tree)->pool = pool;
79251881Speter  (*tree)->node_count = 0;
80251881Speter}
81251881Speter
82251881Speter
83251881Speterstatic svn_error_t *
84251881Spetertree_insert_token(svn_diff__node_t **node, svn_diff__tree_t *tree,
85251881Speter                  void *diff_baton,
86251881Speter                  const svn_diff_fns2_t *vtable,
87251881Speter                  apr_uint32_t hash, void *token)
88251881Speter{
89251881Speter  svn_diff__node_t *new_node;
90251881Speter  svn_diff__node_t **node_ref;
91251881Speter  svn_diff__node_t *parent;
92251881Speter  int rv;
93251881Speter
94251881Speter  SVN_ERR_ASSERT(token);
95251881Speter
96251881Speter  parent = NULL;
97251881Speter  node_ref = &tree->root[hash % SVN_DIFF__HASH_SIZE];
98251881Speter
99251881Speter  while (*node_ref != NULL)
100251881Speter    {
101251881Speter      parent = *node_ref;
102251881Speter
103251881Speter      rv = hash - parent->hash;
104251881Speter      if (!rv)
105251881Speter        SVN_ERR(vtable->token_compare(diff_baton, parent->token, token, &rv));
106251881Speter
107251881Speter      if (rv == 0)
108251881Speter        {
109251881Speter          /* Discard the previous token.  This helps in cases where
110251881Speter           * only recently read tokens are still in memory.
111251881Speter           */
112251881Speter          if (vtable->token_discard != NULL)
113251881Speter            vtable->token_discard(diff_baton, parent->token);
114251881Speter
115251881Speter          parent->token = token;
116251881Speter          *node = parent;
117251881Speter
118251881Speter          return SVN_NO_ERROR;
119251881Speter        }
120251881Speter      else if (rv > 0)
121251881Speter        {
122251881Speter          node_ref = &parent->left;
123251881Speter        }
124251881Speter      else
125251881Speter        {
126251881Speter          node_ref = &parent->right;
127251881Speter        }
128251881Speter    }
129251881Speter
130251881Speter  /* Create a new node */
131251881Speter  new_node = apr_palloc(tree->pool, sizeof(*new_node));
132251881Speter  new_node->parent = parent;
133251881Speter  new_node->left = NULL;
134251881Speter  new_node->right = NULL;
135251881Speter  new_node->hash = hash;
136251881Speter  new_node->token = token;
137251881Speter  new_node->index = tree->node_count++;
138251881Speter
139251881Speter  *node = *node_ref = new_node;
140251881Speter
141251881Speter  return SVN_NO_ERROR;
142251881Speter}
143251881Speter
144251881Speter
145251881Speter/*
146251881Speter * Get all tokens from a datasource.  Return the
147251881Speter * last item in the (circular) list.
148251881Speter */
149251881Spetersvn_error_t *
150251881Spetersvn_diff__get_tokens(svn_diff__position_t **position_list,
151251881Speter                     svn_diff__tree_t *tree,
152251881Speter                     void *diff_baton,
153251881Speter                     const svn_diff_fns2_t *vtable,
154251881Speter                     svn_diff_datasource_e datasource,
155251881Speter                     apr_off_t prefix_lines,
156251881Speter                     apr_pool_t *pool)
157251881Speter{
158251881Speter  svn_diff__position_t *start_position;
159251881Speter  svn_diff__position_t *position = NULL;
160251881Speter  svn_diff__position_t **position_ref;
161251881Speter  svn_diff__node_t *node;
162251881Speter  void *token;
163251881Speter  apr_off_t offset;
164251881Speter  apr_uint32_t hash;
165251881Speter
166251881Speter  *position_list = NULL;
167251881Speter
168251881Speter  position_ref = &start_position;
169251881Speter  offset = prefix_lines;
170251881Speter  hash = 0; /* The callback fn doesn't need to touch it per se */
171251881Speter  while (1)
172251881Speter    {
173251881Speter      SVN_ERR(vtable->datasource_get_next_token(&hash, &token,
174251881Speter                                                diff_baton, datasource));
175251881Speter      if (token == NULL)
176251881Speter        break;
177251881Speter
178251881Speter      offset++;
179251881Speter      SVN_ERR(tree_insert_token(&node, tree, diff_baton, vtable, hash, token));
180251881Speter
181251881Speter      /* Create a new position */
182251881Speter      position = apr_palloc(pool, sizeof(*position));
183251881Speter      position->next = NULL;
184251881Speter      position->token_index = node->index;
185251881Speter      position->offset = offset;
186251881Speter
187251881Speter      *position_ref = position;
188251881Speter      position_ref = &position->next;
189251881Speter    }
190251881Speter
191251881Speter  *position_ref = start_position;
192251881Speter
193251881Speter  SVN_ERR(vtable->datasource_close(diff_baton, datasource));
194251881Speter
195251881Speter  *position_list = position;
196251881Speter
197251881Speter  return SVN_NO_ERROR;
198251881Speter}
199