Skip to content

Commit f3b5fa1

Browse files
committed
Librhash: Add rhash_print_magnet_multi() function
1 parent 6cec246 commit f3b5fa1

File tree

3 files changed

+143
-49
lines changed

3 files changed

+143
-49
lines changed

librhash/rhash.c

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -662,19 +662,24 @@ static size_t rhash_get_magnet_url_size(const char* filepath,
662662
return size;
663663
}
664664

665-
RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
666-
rhash context, unsigned hash_mask, int flags)
665+
static size_t rhash_print_magnet_impl(char* output, size_t out_size, const char* filepath,
666+
rhash_context_ext* const ectx, int flags, uint64_t hash_mask)
667667
{
668-
rhash_context_ext* const ectx = (rhash_context_ext*)context;
669668
int i;
670669
const char* begin = output;
670+
hash_mask &= ectx->rc.hash_mask;
671671

672-
if (!IS_EXTENDED_RHASH_ID(hash_mask)) {
673-
hash_mask &= ectx->rc.hash_mask;
674-
}
675-
676-
if (output == NULL)
672+
if (output == NULL) {
677673
return rhash_get_magnet_url_size(filepath, ectx, hash_mask, flags);
674+
}
675+
if (out_size != RHASH_ERROR) {
676+
size_t prefix_size = rhash_get_magnet_url_size(filepath, ectx, 0, flags);
677+
if (out_size < prefix_size) {
678+
errno = ENOMEM;
679+
return 0;
680+
}
681+
out_size -= prefix_size;
682+
}
678683

679684
/* RHPR_NO_MAGNET, RHPR_FILESIZE */
680685
if ((flags & RHPR_NO_MAGNET) == 0) {
@@ -685,7 +690,7 @@ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
685690
if ((flags & RHPR_FILESIZE) != 0) {
686691
strcpy(output, "xl=");
687692
output += 3;
688-
output += rhash_sprintI64(output, context->msg_size);
693+
output += rhash_sprintI64(output, ectx->rc.msg_size);
689694
*(output++) = '&';
690695
}
691696

@@ -698,33 +703,76 @@ RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
698703
}
699704

700705
for (i = 0; i <= 1; i++) {
701-
unsigned bit;
702-
static const unsigned print_first = (RHASH_ED2K | RHASH_AICH);
703-
unsigned hash = (!i ? hash_mask & print_first : hash_mask & ~print_first);
704-
if (!hash)
705-
continue;
706+
static const uint64_t print_first = (RHASH_ED2K | RHASH_AICH);
707+
uint64_t hash = (!i ? hash_mask & print_first : hash_mask & ~print_first);
706708

707709
/* loop through hash values */
708-
for (bit = hash & -(int)hash; bit <= hash; bit <<= 1) {
709-
const char* name;
710-
if ((bit & hash) == 0) continue;
711-
if (!(name = rhash_get_magnet_name(bit))) continue;
712-
710+
for (; hash; hash = hash & (hash - 1)) {
711+
uint64_t bit = hash & -(int)hash;
712+
const char* magnet_name = rhash_get_magnet_name(bit);
713+
size_t name_length;
714+
if (!magnet_name)
715+
continue; /* silenly skip unsupported hash_id */
716+
name_length = strlen(magnet_name);
717+
if (out_size != RHASH_ERROR) {
718+
size_t hash_part_size = (7 + 2) + name_length +
719+
rhash_print(NULL, &ectx->rc, bit,
720+
(bit & RHASH_SHA1 ? RHPR_BASE32 : 0));
721+
if (out_size < hash_part_size) {
722+
errno = ENOMEM;
723+
return 0;
724+
}
725+
out_size -= hash_part_size;
726+
}
713727
strcpy(output, "xt=urn:");
714728
output += 7;
715-
strcpy(output, name);
716-
output += strlen(name);
729+
strcpy(output, magnet_name);
730+
output += name_length;
717731
*(output++) = ':';
718-
output += rhash_print(output, context, bit,
732+
output += rhash_print(output, &ectx->rc, bit,
719733
(bit & RHASH_SHA1 ? flags | RHPR_BASE32 : flags));
720734
*(output++) = '&';
721735
}
722736
}
723737
output[-1] = '\0'; /* terminate the line */
724-
725738
return (output - begin);
726739
}
727740

741+
RHASH_API size_t rhash_print_magnet_multi(char* output, size_t out_size, const char* filepath,
742+
rhash context, int flags, size_t count, const unsigned hash_ids[])
743+
{
744+
uint64_t hash_mask = 0;
745+
if (!context) {
746+
errno = EINVAL;
747+
return 0;
748+
}
749+
if (count == 0) {
750+
hash_mask = RHASH_ALL_HASHES;
751+
} else {
752+
size_t i;
753+
for (i = 0; i < count; i++) {
754+
if (!IS_VALID_HASH_ID(hash_ids[i]) && (i != 0 || hash_ids[i] != RHASH_ALL_HASHES)) {
755+
errno = EINVAL;
756+
return 0;
757+
}
758+
hash_mask |= (uint64_t)hash_ids[i];
759+
}
760+
}
761+
return rhash_print_magnet_impl(output, out_size, filepath,
762+
(rhash_context_ext*)context, flags, hash_mask);
763+
}
764+
765+
RHASH_API size_t rhash_print_magnet(char* output, const char* filepath,
766+
rhash context, unsigned hash_mask, int flags)
767+
{
768+
if (!context || IS_EXTENDED_RHASH_ID(hash_mask)) {
769+
errno = EINVAL;
770+
return 0;
771+
}
772+
return rhash_print_magnet_impl(output, RHASH_ERROR, filepath,
773+
(rhash_context_ext*)context, flags, (uint64_t)hash_mask);
774+
}
775+
728776

729777
/* HASH SUM OUTPUT INTERFACE */
730778

librhash/rhash.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,27 @@ RHASH_API size_t rhash_print(char* output, rhash context, unsigned hash_id,
403403
* Print magnet link with given filepath and calculated message digest into the
404404
* output buffer. The hash_mask can limit which message digests will be printed.
405405
* The function returns the size of the required buffer.
406-
* If output is NULL the .
406+
* If output is NULL then the size of the required buffer is returned.
407+
*
408+
* @param output a string buffer to receive the magnet link or NULL
409+
* @param out_size size of the output string buffer
410+
* @param filepath the file path to be printed or NULL
411+
* @param context algorithms state
412+
* @param flags can be combination of bits RHPR_UPPERCASE, RHPR_NO_MAGNET,
413+
* RHPR_FILESIZE
414+
* @param count the size of the hash_ids array
415+
* @param hash_ids array of identifiers of message digests to add to the magnet link
416+
* @return number of written characters, including terminating '\0' on success, 0 on fail
417+
*/
418+
RHASH_API size_t rhash_print_magnet_multi(char* output, size_t size, const char* filepath,
419+
rhash context, int flags, size_t count, const unsigned hash_ids[]);
420+
421+
/**
422+
* Deprecated function.
423+
* Print magnet link with given filepath and calculated message digest into the
424+
* output buffer. The hash_mask can limit which message digests will be printed.
425+
* The function returns the size of the required buffer.
426+
* If output is NULL then the size of the required buffer is returned.
407427
*
408428
* @param output a string buffer to receive the magnet link or NULL
409429
* @param filepath the file path to be printed or NULL

librhash/test_lib.c

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "rhash.h"
3131
#include "test_lib.h"
3232

33+
#define COUNTOF(a) (sizeof(a) / sizeof(*a))
34+
3335
/*=========================================================================*
3436
* Test vectors *
3537
*=========================================================================*/
@@ -893,7 +895,7 @@ static void test_long_strings(void)
893895
dbg("test long strings\n");
894896

895897
/* test all algorithms on 1,000,000 characters of 'a' */
896-
for (count = 0; count < (sizeof(tests) / sizeof(id_to_hash_t)); count++) {
898+
for (count = 0; count < COUNTOF(tests); count++) {
897899
unsigned flags = (tests[count].hash_id == RHASH_BTIH ? CHDT_SET_FILENAME : CHDT_NO_FLAGS);
898900
assert_rep_hash(tests[count].hash_id, 'a', 1000000, tests[count].expected_hash, flags);
899901
}
@@ -1274,22 +1276,42 @@ static void test_import_export(void)
12741276
#endif /* !defined(NO_IMPORT_EXPORT) */
12751277
}
12761278

1279+
static uint64_t make_hash_mask(size_t count, unsigned hash_ids[])
1280+
{
1281+
uint64_t hash_mask = 0;
1282+
size_t i;
1283+
for (i = 0; i < count; i++) {
1284+
hash_mask |= hash_ids[i];
1285+
}
1286+
return hash_mask;
1287+
}
1288+
12771289
#define TEST_PATH 0x4000000
12781290

12791291
/**
12801292
* Verify a magnet link.
12811293
*/
1282-
static void assert_magnet(const char* expected,
1283-
rhash ctx, unsigned mask, int flags)
1294+
static void test_magnet(const char* expected,
1295+
rhash ctx, int flags, size_t count, unsigned hash_ids[])
12841296
{
12851297
static char out[2800];
12861298
const char* path = (flags & TEST_PATH ? "test.txt" : NULL);
1287-
size_t size_calculated = rhash_print_magnet(NULL, path, ctx, mask, flags);
1288-
size_t size_printed;
1299+
uint64_t hash_mask = make_hash_mask(count, hash_ids);
1300+
size_t size_calculated = rhash_print_magnet_multi(NULL, 0, path, ctx, flags, count, hash_ids);
1301+
size_t size_calculated_legacy = rhash_print_magnet(NULL, path, ctx, (unsigned)hash_mask, flags);
1302+
size_t size_printed, size_printed_legacy;
12891303
REQUIRE_TRUE(size_calculated < sizeof(out), "too small buffer for magnet link\n");
1304+
CHECK_NE(0, size_calculated, "non zero buffer size expected to be returned\n");
1305+
CHECK_EQ(size_calculated, size_calculated_legacy, "wrong size_calculated_legacy\n");
12901306

12911307
flags &= ~TEST_PATH;
1292-
size_printed = rhash_print_magnet(out, path, ctx, mask, flags);
1308+
if (size_calculated > 0) {
1309+
size_printed = rhash_print_magnet_multi(out, size_calculated - 1, path, ctx, flags, count, hash_ids);
1310+
CHECK_EQ(0, size_printed, "too small buffer error expected, but not occurred\n");
1311+
}
1312+
size_printed = rhash_print_magnet_multi(out, size_calculated, path, ctx, flags, count, hash_ids);
1313+
size_printed_legacy = rhash_print_magnet(out, path, ctx, (unsigned)hash_mask, flags);
1314+
CHECK_EQ(size_printed, size_printed_legacy, "wrong size_calculated_legacy\n");
12931315

12941316
if (expected && strcmp(expected, out) != 0) {
12951317
log_error2("\"%s\" != \"%s\"\n", expected, out);
@@ -1308,44 +1330,48 @@ static void assert_magnet(const char* expected,
13081330
/**
13091331
* Test printing of magnet links.
13101332
*/
1311-
static void test_magnet(void)
1333+
static void test_magnet_links(void)
13121334
{
1313-
unsigned all_hash_ids[RHASH_HASH_COUNT];
1314-
unsigned crc32_and_tth_ids[2] = { RHASH_CRC32, RHASH_TTH };
1315-
size_t count = rhash_get_all_algorithms(RHASH_HASH_COUNT, all_hash_ids);
1335+
unsigned hash_ids_all[RHASH_HASH_COUNT];
1336+
unsigned hash_ids_tth[] = { RHASH_TTH };
1337+
unsigned hash_ids_md5[] = { RHASH_MD5 };
1338+
unsigned hash_ids_special[] = { RHASH_ED2K, RHASH_AICH, RHASH_SHA1, RHASH_BTIH };
1339+
unsigned hash_ids_crc32_tth[2] = { RHASH_CRC32, RHASH_TTH };
1340+
unsigned hash_id_all_hashes = RHASH_ALL_HASHES;
1341+
size_t count = rhash_get_all_algorithms(RHASH_HASH_COUNT, hash_ids_all);
13161342
size_t i;
13171343
rhash ctx;
1318-
dbg("test magnet link\n");
1319-
ctx = rhash_init(RHASH_ALL_HASHES);
1344+
dbg("test magnet links\n");
1345+
ctx = rhash_init_multi(count, hash_ids_all);
13201346
rhash_update(ctx, "a", 1);
13211347
rhash_final(ctx, 0);
1322-
assert_magnet("magnet:?xl=1&dn=test.txt&xt=urn:tree:tiger:czquwh3iyxbf5l3bgyugzhassmxu647ip2ike4y",
1323-
ctx, RHASH_TTH, RHPR_FILESIZE | TEST_PATH);
1324-
assert_magnet("magnet:?xl=1&xt=urn:md5:0CC175B9C0F1B6A831C399E269772661",
1325-
ctx, RHASH_MD5, RHPR_FILESIZE | RHPR_UPPERCASE);
1326-
assert_magnet(
1348+
test_magnet("magnet:?xl=1&dn=test.txt&xt=urn:tree:tiger:czquwh3iyxbf5l3bgyugzhassmxu647ip2ike4y",
1349+
ctx, RHPR_FILESIZE | TEST_PATH, COUNTOF(hash_ids_tth), hash_ids_tth);
1350+
test_magnet("magnet:?xl=1&xt=urn:md5:0CC175B9C0F1B6A831C399E269772661",
1351+
ctx, RHPR_FILESIZE | RHPR_UPPERCASE, COUNTOF(hash_ids_md5), hash_ids_md5);
1352+
test_magnet(
13271353
"xt=urn:ed2k:bde52cb31de33e46245e05fbdbd6fb24&"
13281354
"xt=urn:aich:q336in72uwt7zyk5dxolt2xk5i3xmz5y&"
13291355
"xt=urn:sha1:q336in72uwt7zyk5dxolt2xk5i3xmz5y&"
13301356
"xt=urn:btih:827cd89846fc132e2e67e29c2784c65443bb4dc1",
1331-
ctx, RHASH_ED2K | RHASH_AICH | RHASH_SHA1 | RHASH_BTIH, RHPR_NO_MAGNET);
1357+
ctx, RHPR_NO_MAGNET, COUNTOF(hash_ids_special), hash_ids_special);
13321358

13331359
/* verify length calculation for all hashes */
13341360
for (i = 0; i < count; i++) {
1335-
unsigned hash_id = all_hash_ids[i];
1336-
assert_magnet(NULL, ctx, hash_id, RHPR_FILESIZE | RHPR_NO_MAGNET);
1361+
unsigned hash_id = hash_ids_all[i];
1362+
test_magnet(NULL, ctx, RHPR_FILESIZE | RHPR_NO_MAGNET, 1, &hash_id);
13371363
}
1338-
assert_magnet(NULL, ctx, RHASH_ALL_HASHES, RHPR_FILESIZE | RHPR_NO_MAGNET);
1364+
test_magnet(NULL, ctx, RHPR_FILESIZE | RHPR_NO_MAGNET, 1, &hash_id_all_hashes);
13391365
rhash_free(ctx);
13401366

13411367
/* test with two hash functions */
1342-
ctx = rhash_init_multi(2, crc32_and_tth_ids);
1368+
ctx = rhash_init_multi(COUNTOF(hash_ids_crc32_tth), hash_ids_crc32_tth);
13431369
rhash_update(ctx, "abc", 3);
13441370
rhash_final(ctx, 0);
1345-
assert_magnet(
1371+
test_magnet(
13461372
"magnet:?xl=3&xt=urn:crc32:352441c2&"
13471373
"xt=urn:tree:tiger:asd4ujseh5m47pdyb46kbtsqtsgdklbhyxomuia",
1348-
ctx, RHASH_ALL_HASHES, RHPR_FILESIZE);
1374+
ctx, RHPR_FILESIZE, 1, &hash_id_all_hashes);
13491375
}
13501376

13511377
/**
@@ -1465,7 +1491,7 @@ int main(int argc, char* argv[])
14651491
test_id_getters();
14661492
test_get_context();
14671493
test_import_export();
1468-
test_magnet();
1494+
test_magnet_links();
14691495
if (g_errors_count == 0)
14701496
printf("All sums are working properly!\n");
14711497
fflush(stdout);

0 commit comments

Comments
 (0)