1 /*
2  * ggit-oid.c
3  * This file is part of libgit2-glib
4  *
5  * Copyright (C) 2011 - Ignacio Casal Quinteiro
6  *
7  * libgit2-glib is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * libgit2-glib is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with libgit2-glib. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <git2.h>
22 
23 #include "ggit-oid.h"
24 
25 struct _GgitOId
26 {
27 	git_oid oid;
28 };
29 
G_DEFINE_BOXED_TYPE(GgitOId,ggit_oid,ggit_oid_copy,ggit_oid_free)30 G_DEFINE_BOXED_TYPE (GgitOId, ggit_oid, ggit_oid_copy, ggit_oid_free)
31 
32 GgitOId *
33 _ggit_oid_wrap (const git_oid *oid)
34 {
35 	GgitOId *glib_oid;
36 
37 	glib_oid = g_slice_new (GgitOId);
38 	git_oid_cpy (&glib_oid->oid, oid);
39 
40 	return glib_oid;
41 }
42 
43 const git_oid *
_ggit_oid_get_oid(GgitOId * oid)44 _ggit_oid_get_oid (GgitOId *oid)
45 {
46 	return (const git_oid *)&oid->oid;
47 }
48 
49 /**
50  * ggit_oid_copy:
51  * @oid: a #GgitOId.
52  *
53  * Copies @oid into a newly allocated #GgitOId.
54  *
55  * Returns: (transfer full) (nullable): a newly allocated #GgitOId.
56  */
57 GgitOId *
ggit_oid_copy(GgitOId * oid)58 ggit_oid_copy (GgitOId *oid)
59 {
60 	g_return_val_if_fail (oid != NULL, NULL);
61 
62 	return _ggit_oid_wrap (&oid->oid);
63 }
64 
65 /**
66  * ggit_oid_free:
67  * @oid: a #GgitOId.
68  *
69  * Frees @oid.
70  */
71 void
ggit_oid_free(GgitOId * oid)72 ggit_oid_free (GgitOId *oid)
73 {
74 	g_return_if_fail (oid != NULL);
75 
76 	g_slice_free (GgitOId, oid);
77 }
78 
79 /**
80  * ggit_oid_new_from_string:
81  * @str: input hex string; must be pointing at the start of
82  *       the hex sequence and have at least the number of bytes
83  *       needed for an oid encoded in hex (40 bytes).
84  *
85  * Parses a hex formatted object id into a #GgitOId.
86  *
87  * Returns: (transfer full) (nullable): a newly allocated #GgitOId or %NULL on error.
88  */
89 GgitOId *
ggit_oid_new_from_string(const gchar * str)90 ggit_oid_new_from_string (const gchar *str)
91 {
92 	GgitOId *glib_oid = NULL;
93 	git_oid oid;
94 
95 	g_return_val_if_fail (str != NULL, NULL);
96 
97 	if (git_oid_fromstr (&oid, str) == GIT_OK)
98 	{
99 		glib_oid = _ggit_oid_wrap (&oid);
100 	}
101 
102 	return glib_oid;
103 }
104 
105 /**
106  * ggit_oid_new_from_raw:
107  * @raw: (array zero-terminated=1) (element-type guchar): the raw input bytes to be copied.
108  *
109  * Creates a new #GgitOId from a raw oid.
110  *
111  * Returns: (transfer full) (nullable): a newly allocated #GgitOId or %NULL on error.
112  */
113 GgitOId *
ggit_oid_new_from_raw(const guchar * raw)114 ggit_oid_new_from_raw (const guchar *raw)
115 {
116 	git_oid oid;
117 
118 	g_return_val_if_fail (raw != NULL, NULL);
119 
120 	git_oid_fromraw (&oid, raw);
121 
122 	return _ggit_oid_wrap (&oid);
123 }
124 
125 /**
126  * ggit_oid_compare:
127  * @a: first #GgitOId.
128  * @b: second #GgitOId.
129  *
130  * Compare two #GgitOId structures.
131  *
132  * Returns: <0, 0, >0 if a < b, a == b, a > b.
133  */
134 gint
ggit_oid_compare(GgitOId * a,GgitOId * b)135 ggit_oid_compare (GgitOId *a,
136                   GgitOId *b)
137 {
138 	g_return_val_if_fail (a != NULL, 0);
139 	g_return_val_if_fail (b != NULL, 0);
140 
141 	return git_oid_cmp (&a->oid, &b->oid);
142 }
143 
144 /**
145  * ggit_oid_to_string:
146  * @oid: a #GgitOId.
147  *
148  * Converts @oid into a readable string.
149  *
150  * Returns: (transfer full) (nullable): a newly allocated string representing @oid or %NULL.
151  */
152 gchar *
ggit_oid_to_string(GgitOId * oid)153 ggit_oid_to_string (GgitOId *oid)
154 {
155 	gchar *hex;
156 
157 	g_return_val_if_fail (oid != NULL, NULL);
158 
159 	hex = g_new (char, GIT_OID_HEXSZ + 1);
160 
161 	return git_oid_tostr (hex, GIT_OID_HEXSZ + 1, &oid->oid);
162 }
163 
164 /**
165  * ggit_oid_hash:
166  * @oid: a #GgitOId.
167  *
168  * Computes a hash value for a git object identifier.
169  *
170  * Returns: the hash value
171  *
172  **/
173 guint
ggit_oid_hash(GgitOId const * oid)174 ggit_oid_hash (GgitOId const *oid)
175 {
176 	/* This is copied from glib
177 	 * This function implements the widely used "djb" hash apparently posted
178 	 * by Daniel Bernstein to comp.lang.c some time ago.  The 32 bit
179 	 * unsigned hash value starts at 5381 and for each byte 'c' in the
180 	 * string, is updated: <literal>hash = hash * 33 + c</literal>.  This
181 	 * function uses the signed value of each byte.
182 	 */
183 
184 	guint32 h = 5381;
185 	guint i;
186 
187 	for (i = 0; i < GIT_OID_RAWSZ; ++i)
188 	{
189 		h = (h << 5) + h + oid->oid.id[i];
190 	}
191 
192 	return h;
193 }
194 
195 /**
196  * ggit_oid_equal:
197  * @a: a #GgitOId.
198  * @b: a #GgitOId.
199  *
200  * Compares two #GgitOId for equality.
201  *
202  * Returns: %TRUE if @a and @b are equal, %FALSE otherwise
203  *
204  **/
205 gboolean
ggit_oid_equal(GgitOId const * a,GgitOId const * b)206 ggit_oid_equal (GgitOId const *a,
207                 GgitOId const *b)
208 {
209 	if ((a != NULL) != (b != NULL))
210 	{
211 		return FALSE;
212 	}
213 	else if (a == b)
214 	{
215 		return TRUE;
216 	}
217 
218 	return git_oid_cmp (&a->oid, &b->oid) == 0;
219 }
220 
221 /**
222  * ggit_oid_is_zero:
223  * @oid: a #GgitOId.
224  *
225  * Get whether the oid contains only zeros.
226  *
227  * Returns: %TRUE if the oid contains only zeros, %FALSE otherwise.
228  */
229 gboolean
ggit_oid_is_zero(GgitOId const * oid)230 ggit_oid_is_zero (GgitOId const *oid)
231 {
232 	g_return_val_if_fail (oid != NULL, FALSE);
233 
234 	return git_oid_iszero (&oid->oid) == 1;
235 }
236 
237 static gint
c_to_h(gchar c)238 c_to_h (gchar c)
239 {
240 	if (c >= '0' && c <= '9')
241 	{
242 		return c - '0';
243 	}
244 	else if (c >= 'A' && c <= 'F')
245 	{
246 		return 10 + (c - 'A');
247 	}
248 	else if (c >= 'a' && c <= 'f')
249 	{
250 		return 10 + (c - 'a');
251 	}
252 	else
253 	{
254 		return -1;
255 	}
256 }
257 
258 /**
259  * ggit_oid_has_prefix:
260  * @oid: a #GgitOId.
261  * @prefix: a prefix.
262  *
263  * Check whether the object id has a given prefix. Note that the prefix is
264  * specified in hexadecimal ASCII.
265  *
266  * Returns: %TRUE if the id has the given prefix, %FALSE otherwise.
267  */
268 gboolean
ggit_oid_has_prefix(GgitOId * oid,const gchar * prefix)269 ggit_oid_has_prefix (GgitOId     *oid,
270                      const gchar *prefix)
271 {
272 	gint i;
273 
274 	for (i = 0; i < GIT_OID_RAWSZ; ++i)
275 	{
276 		gint v1;
277 
278 		if (*prefix == '\0')
279 		{
280 			return TRUE;
281 		}
282 
283 		v1 = c_to_h (*prefix++);
284 
285 		if (v1 == -1)
286 		{
287 			return FALSE;
288 		}
289 
290 		if (*prefix != '\0')
291 		{
292 			gint v2;
293 
294 			v2 = c_to_h (*prefix++);
295 
296 			if (v2 == -1)
297 			{
298 				return FALSE;
299 			}
300 
301 			if (oid->oid.id[i] != (v1 << 4) + v2)
302 			{
303 				return FALSE;
304 			}
305 		}
306 		else if (oid->oid.id[i] >> 4 != v1)
307 		{
308 			return FALSE;
309 		}
310 	}
311 
312 	return *prefix == '\0';
313 }
314 
315 /* ex:set ts=8 noet: */
316