Compare commits
1 Commits
zap-amount
...
transactio
| Author | SHA1 | Date | |
|---|---|---|---|
|
a6835b835b
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,4 +6,3 @@ damus.xcodeproj/xcshareddata/xcbaselines
|
|||||||
TODO.bak
|
TODO.bak
|
||||||
tags
|
tags
|
||||||
build-git-hash.txt
|
build-git-hash.txt
|
||||||
.build
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ struct NotificationFormatter {
|
|||||||
content.title = Self.zap_notification_title(zap)
|
content.title = Self.zap_notification_title(zap)
|
||||||
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
|
content.body = Self.zap_notification_body(profiles: state.profiles, zap: zap)
|
||||||
content.sound = UNNotificationSound.default
|
content.sound = UNNotificationSound.default
|
||||||
content.userInfo = LossyLocalNotification(type: .zap, mention: .init(nip19: .note(notify.event.id))).to_user_info()
|
content.userInfo = LossyLocalNotification(type: .zap, mention: .note(notify.event.id)).to_user_info()
|
||||||
return (content, "myZapNotification")
|
return (content, "myZapNotification")
|
||||||
default:
|
default:
|
||||||
// The sync method should have taken care of this.
|
// The sync method should have taken care of this.
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class NotificationService: UNNotificationServiceExtension {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let notification_object = generate_local_notification_object(ndb: state.ndb, from: nostr_event, state: state) else {
|
guard let notification_object = generate_local_notification_object(from: nostr_event, state: state) else {
|
||||||
Log.debug("generate_local_notification_object failed", for: .push_notifications)
|
Log.debug("generate_local_notification_object failed", for: .push_notifications)
|
||||||
// We could not process this notification. Probably an unsupported nostr event kind. Suppress.
|
// We could not process this notification. Probably an unsupported nostr event kind. Suppress.
|
||||||
// contentHandler(UNNotificationContent())
|
// contentHandler(UNNotificationContent())
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -1,26 +1,10 @@
|
|||||||
<div align="center">
|
[](https://github.com/damus-io/damus/actions/workflows/run-tests.yaml)
|
||||||
|
|
||||||
<img src="./damus/Assets.xcassets/damus-home.imageset/damus-home@2x.png" alt="Damus Logo" title="Damus logo" width=""/>
|
# damus
|
||||||
|
|
||||||
# Damus
|
|
||||||
|
|
||||||
The social network you control
|
|
||||||
|
|
||||||
A twitter-like [nostr][nostr] client for iPhone, iPad and MacOS.
|
A twitter-like [nostr][nostr] client for iPhone, iPad and MacOS.
|
||||||
|
|
||||||
[](/LICENSE)
|
<img src="./ss.png" width="50%" height="50%" />
|
||||||
|
|
||||||
## Download and Install
|
|
||||||
|
|
||||||
[](https://apps.apple.com/us/app/damus/id1628663131)
|
|
||||||
|
|
||||||
## Supported Platforms
|
|
||||||
|
|
||||||
iOS 16.0+ • macOS 13.0+
|
|
||||||
|
|
||||||
<img src="./demo1.png" width="70%" height="50%" />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
[nostr]: https://github.com/fiatjaf/nostr
|
[nostr]: https://github.com/fiatjaf/nostr
|
||||||
|
|
||||||
|
|||||||
57
damus-c/block.h
Normal file
57
damus-c/block.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// block.h
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-04-09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef block_h
|
||||||
|
#define block_h
|
||||||
|
|
||||||
|
#include "nostr_bech32.h"
|
||||||
|
#include "str_block.h"
|
||||||
|
|
||||||
|
#define MAX_BLOCKS 1024
|
||||||
|
|
||||||
|
enum block_type {
|
||||||
|
BLOCK_HASHTAG = 1,
|
||||||
|
BLOCK_TEXT = 2,
|
||||||
|
BLOCK_MENTION_INDEX = 3,
|
||||||
|
BLOCK_MENTION_BECH32 = 4,
|
||||||
|
BLOCK_URL = 5,
|
||||||
|
BLOCK_INVOICE = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct invoice_block {
|
||||||
|
struct str_block invstr;
|
||||||
|
union {
|
||||||
|
struct bolt11 *bolt11;
|
||||||
|
};
|
||||||
|
} invoice_block_t;
|
||||||
|
|
||||||
|
typedef struct mention_bech32_block {
|
||||||
|
struct str_block str;
|
||||||
|
struct nostr_bech32 bech32;
|
||||||
|
} mention_bech32_block_t;
|
||||||
|
|
||||||
|
typedef struct note_block {
|
||||||
|
enum block_type type;
|
||||||
|
union {
|
||||||
|
struct str_block str;
|
||||||
|
struct invoice_block invoice;
|
||||||
|
struct mention_bech32_block mention_bech32;
|
||||||
|
int mention_index;
|
||||||
|
} block;
|
||||||
|
} block_t;
|
||||||
|
|
||||||
|
typedef struct note_blocks {
|
||||||
|
int words;
|
||||||
|
int num_blocks;
|
||||||
|
struct note_block *blocks;
|
||||||
|
} blocks_t;
|
||||||
|
|
||||||
|
void blocks_init(struct note_blocks *blocks);
|
||||||
|
void blocks_free(struct note_blocks *blocks);
|
||||||
|
|
||||||
|
#endif /* block_h */
|
||||||
@@ -2,20 +2,28 @@
|
|||||||
#ifndef JB55_CURSOR_H
|
#ifndef JB55_CURSOR_H
|
||||||
#define JB55_CURSOR_H
|
#define JB55_CURSOR_H
|
||||||
|
|
||||||
#include "ccan/likely/likely.h"
|
#include "typedefs.h"
|
||||||
|
#include "varint.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <inttypes.h>
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#define unlikely(x) __builtin_expect((x),0)
|
||||||
|
#define likely(x) __builtin_expect((x),1)
|
||||||
|
|
||||||
struct cursor {
|
struct cursor {
|
||||||
unsigned char *start;
|
unsigned char *start;
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
unsigned char *end;
|
unsigned char *end;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct array {
|
||||||
|
struct cursor cur;
|
||||||
|
unsigned int elem_size;
|
||||||
|
};
|
||||||
|
|
||||||
static inline void reset_cursor(struct cursor *cursor)
|
static inline void reset_cursor(struct cursor *cursor)
|
||||||
{
|
{
|
||||||
cursor->p = cursor->start;
|
cursor->p = cursor->start;
|
||||||
@@ -27,13 +35,19 @@ static inline void wipe_cursor(struct cursor *cursor)
|
|||||||
memset(cursor->start, 0, cursor->end - cursor->start);
|
memset(cursor->start, 0, cursor->end - cursor->start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void make_cursor(unsigned char *start, unsigned char *end, struct cursor *cursor)
|
static inline void make_cursor(u8 *start, u8 *end, struct cursor *cursor)
|
||||||
{
|
{
|
||||||
cursor->start = start;
|
cursor->start = start;
|
||||||
cursor->p = start;
|
cursor->p = start;
|
||||||
cursor->end = end;
|
cursor->end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void make_array(struct array *a, u8* start, u8 *end, unsigned int elem_size)
|
||||||
|
{
|
||||||
|
make_cursor(start, end, &a->cur);
|
||||||
|
a->elem_size = elem_size;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int cursor_eof(struct cursor *c)
|
static inline int cursor_eof(struct cursor *c)
|
||||||
{
|
{
|
||||||
return c->p == c->end;
|
return c->p == c->end;
|
||||||
@@ -66,7 +80,7 @@ static inline void *cursor_alloc(struct cursor *mem, unsigned long size)
|
|||||||
|
|
||||||
static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
|
static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size)
|
||||||
{
|
{
|
||||||
unsigned char *p;
|
u8 *p;
|
||||||
if (!(p = cursor_alloc(mem, size))) {
|
if (!(p = cursor_alloc(mem, size))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -74,16 +88,6 @@ static inline int cursor_slice(struct cursor *mem, struct cursor *slice, size_t
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_malloc_slice(struct cursor *mem, struct cursor *slice, size_t size)
|
|
||||||
{
|
|
||||||
unsigned char *p;
|
|
||||||
if (!(p = cursor_malloc(mem, size))) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
make_cursor(p, mem->p, slice);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void copy_cursor(struct cursor *src, struct cursor *dest)
|
static inline void copy_cursor(struct cursor *src, struct cursor *dest)
|
||||||
{
|
{
|
||||||
@@ -102,7 +106,7 @@ static inline int cursor_skip(struct cursor *cursor, int n)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_pull_byte(struct cursor *cursor, unsigned char *c)
|
static inline int pull_byte(struct cursor *cursor, u8 *c)
|
||||||
{
|
{
|
||||||
if (unlikely(cursor->p >= cursor->end))
|
if (unlikely(cursor->p >= cursor->end))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -113,7 +117,7 @@ static inline int cursor_pull_byte(struct cursor *cursor, unsigned char *c)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int parse_byte(struct cursor *cursor, unsigned char *c)
|
static inline int parse_byte(struct cursor *cursor, u8 *c)
|
||||||
{
|
{
|
||||||
if (unlikely(cursor->p >= cursor->end))
|
if (unlikely(cursor->p >= cursor->end))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -158,7 +162,7 @@ static inline int cursor_pull_c_str(struct cursor *cursor, const char **str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline int cursor_push_byte(struct cursor *cursor, unsigned char c)
|
static inline int cursor_push_byte(struct cursor *cursor, u8 c)
|
||||||
{
|
{
|
||||||
if (unlikely(cursor->p + 1 > cursor->end)) {
|
if (unlikely(cursor->p + 1 > cursor->end)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -170,7 +174,7 @@ static inline int cursor_push_byte(struct cursor *cursor, unsigned char c)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_pull(struct cursor *cursor, unsigned char *data, int len)
|
static inline int cursor_pull(struct cursor *cursor, u8 *data, int len)
|
||||||
{
|
{
|
||||||
if (unlikely(cursor->p + len > cursor->end)) {
|
if (unlikely(cursor->p + len > cursor->end)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -240,7 +244,7 @@ static inline unsigned char *cursor_top(struct cursor *cur, int len)
|
|||||||
|
|
||||||
static inline int cursor_top_int(struct cursor *cur, int *i)
|
static inline int cursor_top_int(struct cursor *cur, int *i)
|
||||||
{
|
{
|
||||||
unsigned char *p;
|
u8 *p;
|
||||||
if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) {
|
if (unlikely(!(p = cursor_top(cur, sizeof(*i))))) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -248,7 +252,7 @@ static inline int cursor_top_int(struct cursor *cur, int *i)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_pop(struct cursor *cur, unsigned char *data, int len)
|
static inline int cursor_pop(struct cursor *cur, u8 *data, int len)
|
||||||
{
|
{
|
||||||
if (unlikely(cur->p - len < cur->start)) {
|
if (unlikely(cur->p - len < cur->start)) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -260,9 +264,9 @@ static inline int cursor_pop(struct cursor *cur, unsigned char *data, int len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_push(struct cursor *cursor, unsigned char *data, int len)
|
static inline int cursor_push(struct cursor *cursor, u8 *data, int len)
|
||||||
{
|
{
|
||||||
if (unlikely(cursor->p + len > cursor->end)) {
|
if (unlikely(cursor->p + len >= cursor->end)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +280,7 @@ static inline int cursor_push(struct cursor *cursor, unsigned char *data, int le
|
|||||||
|
|
||||||
static inline int cursor_push_int(struct cursor *cursor, int i)
|
static inline int cursor_push_int(struct cursor *cursor, int i)
|
||||||
{
|
{
|
||||||
return cursor_push(cursor, (unsigned char*)&i, sizeof(i));
|
return cursor_push(cursor, (u8*)&i, sizeof(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
|
static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
|
||||||
@@ -284,79 +288,73 @@ static inline size_t cursor_count(struct cursor *cursor, size_t elem_size)
|
|||||||
return (cursor->p - cursor->start)/elem_size;
|
return (cursor->p - cursor->start)/elem_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Encodes a 64-bit integer into a variable-length format and pushes it into a cursor.
|
/* TODO: push_varint */
|
||||||
* Returns the number of bytes used or -1 in case of an error. */
|
static inline int push_varint(struct cursor *cursor, int n)
|
||||||
static inline int cursor_push_varint(struct cursor *cursor, uint64_t n)
|
|
||||||
{
|
{
|
||||||
int len = 0;
|
int ok, len;
|
||||||
do {
|
unsigned char b;
|
||||||
unsigned char b = (n & 0x7F) | (n > 0x7F ? 0x80 : 0);
|
len = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
b = (n & 0xFF) | 0x80;
|
||||||
n >>= 7;
|
n >>= 7;
|
||||||
if (!cursor_push_byte(cursor, b))
|
if (n == 0) {
|
||||||
return -1; // Error handling
|
b &= 0x7F;
|
||||||
|
ok = cursor_push_byte(cursor, b);
|
||||||
len++;
|
len++;
|
||||||
} while (n != 0);
|
if (!ok) return 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = cursor_push_byte(cursor, b);
|
||||||
|
len++;
|
||||||
|
if (!ok) return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_pull_varint(struct cursor *cursor, uint64_t *n)
|
/* TODO: pull_varint */
|
||||||
|
static inline int pull_varint(struct cursor *cursor, int *n)
|
||||||
{
|
{
|
||||||
int ok, i;
|
int ok, i;
|
||||||
unsigned char b;
|
unsigned char b;
|
||||||
|
|
||||||
*n = 0;
|
*n = 0;
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) { // Loop up to 10 bytes for 64-bit
|
for (i = 0;; i++) {
|
||||||
ok = cursor_pull_byte(cursor, &b);
|
ok = pull_byte(cursor, &b);
|
||||||
if (!ok) return 0;
|
if (!ok) return 0;
|
||||||
|
|
||||||
*n |= ((int64_t)b & 0x7F) << (i * 7);
|
*n |= ((int)b & 0x7F) << (i * 7);
|
||||||
|
|
||||||
|
/* is_last */
|
||||||
if ((b & 0x80) == 0) {
|
if ((b & 0x80) == 0) {
|
||||||
return i + 1; // Successfully read i+1 bytes
|
return i+1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 10; // Successfully read 10 bytes for a full 64-bit integer
|
if (i == 4) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cursor_pull_varint_u32(struct cursor *cursor, uint32_t *v)
|
|
||||||
{
|
|
||||||
uint64_t bigval;
|
|
||||||
|
|
||||||
if (!cursor_pull_varint(cursor, &bigval))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (bigval > UINT32_MAX)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
*v = (uint32_t) bigval;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_pull_int(struct cursor *cursor, int *i)
|
static inline int cursor_pull_int(struct cursor *cursor, int *i)
|
||||||
{
|
{
|
||||||
return cursor_pull(cursor, (unsigned char*)i, sizeof(*i));
|
return cursor_pull(cursor, (u8*)i, sizeof(*i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_push_u32(struct cursor *cursor, uint32_t i) {
|
static inline int cursor_push_u32(struct cursor *cursor, uint32_t i) {
|
||||||
return cursor_push(cursor, (unsigned char*)&i, sizeof(i));
|
return cursor_push(cursor, (unsigned char*)&i, sizeof(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_push_u16(struct cursor *cursor, unsigned short i)
|
static inline int cursor_push_u16(struct cursor *cursor, u16 i)
|
||||||
{
|
{
|
||||||
return cursor_push(cursor, (unsigned char*)&i, sizeof(i));
|
return cursor_push(cursor, (u8*)&i, sizeof(i));
|
||||||
}
|
|
||||||
|
|
||||||
static inline int cursor_pull_u16(struct cursor *cursor, uint16_t *i)
|
|
||||||
{
|
|
||||||
return cursor_pull(cursor, (unsigned char*)i, sizeof(*i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size)
|
static inline void *index_cursor(struct cursor *cursor, unsigned int index, int elem_size)
|
||||||
{
|
{
|
||||||
unsigned char *p;
|
u8 *p;
|
||||||
p = &cursor->start[elem_size * index];
|
p = &cursor->start[elem_size * index];
|
||||||
|
|
||||||
if (unlikely(p >= cursor->end))
|
if (unlikely(p >= cursor->end))
|
||||||
@@ -368,7 +366,7 @@ static inline void *index_cursor(struct cursor *cursor, unsigned int index, int
|
|||||||
|
|
||||||
static inline int push_sized_str(struct cursor *cursor, const char *str, int len)
|
static inline int push_sized_str(struct cursor *cursor, const char *str, int len)
|
||||||
{
|
{
|
||||||
return cursor_push(cursor, (unsigned char*)str, len);
|
return cursor_push(cursor, (u8*)str, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_push_lowercase(struct cursor *cur, const char *str, int len)
|
static inline int cursor_push_lowercase(struct cursor *cur, const char *str, int len)
|
||||||
@@ -387,40 +385,41 @@ static inline int cursor_push_lowercase(struct cursor *cur, const char *str, int
|
|||||||
|
|
||||||
static inline int cursor_push_str(struct cursor *cursor, const char *str)
|
static inline int cursor_push_str(struct cursor *cursor, const char *str)
|
||||||
{
|
{
|
||||||
return cursor_push(cursor, (unsigned char*)str, (int)strlen(str));
|
return cursor_push(cursor, (u8*)str, (int)strlen(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_push_c_str(struct cursor *cursor, const char *str)
|
static inline int cursor_push_c_str(struct cursor *cursor, const char *str)
|
||||||
{
|
{
|
||||||
if (str == NULL)
|
|
||||||
return cursor_push_byte(cursor, 0);
|
|
||||||
return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0);
|
return cursor_push_str(cursor, str) && cursor_push_byte(cursor, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: push varint size */
|
/* TODO: push varint size */
|
||||||
static inline int push_prefixed_str(struct cursor *cursor, const char *str)
|
static inline int push_prefixed_str(struct cursor *cursor, const char *str)
|
||||||
{
|
{
|
||||||
uint64_t len;
|
int ok, len;
|
||||||
len = strlen(str);
|
len = (int)strlen(str);
|
||||||
if (!cursor_push_varint(cursor, len))
|
ok = push_varint(cursor, len);
|
||||||
return 0;
|
if (!ok) return 0;
|
||||||
return push_sized_str(cursor, str, len);
|
return push_sized_str(cursor, str, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int pull_prefixed_str(struct cursor *cursor, struct cursor *dest_buf, const char **str)
|
static inline int pull_prefixed_str(struct cursor *cursor, struct cursor *dest_buf, const char **str)
|
||||||
{
|
{
|
||||||
uint64_t len;
|
int len, ok;
|
||||||
|
|
||||||
if (!cursor_pull_varint(cursor, &len))
|
ok = pull_varint(cursor, &len);
|
||||||
|
if (!ok) return 0;
|
||||||
|
|
||||||
|
if (unlikely(dest_buf->p + len > dest_buf->end)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (unlikely(dest_buf->p + len > dest_buf->end))
|
ok = pull_data_into_cursor(cursor, dest_buf, (unsigned char**)str, len);
|
||||||
return 0;
|
if (!ok) return 0;
|
||||||
|
|
||||||
if (!pull_data_into_cursor(cursor, dest_buf, (unsigned char**)str, len))
|
ok = cursor_push_byte(dest_buf, 0);
|
||||||
return 0;
|
|
||||||
|
|
||||||
return cursor_push_byte(dest_buf, 0);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int cursor_remaining_capacity(struct cursor *cursor)
|
static inline int cursor_remaining_capacity(struct cursor *cursor)
|
||||||
@@ -454,7 +453,7 @@ static inline void cursor_print_around(struct cursor *cur, int range)
|
|||||||
}
|
}
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
static inline int pull_bytes(struct cursor *cur, int count, const unsigned char **bytes) {
|
static inline int pull_bytes(struct cursor *cur, int count, const u8 **bytes) {
|
||||||
if (cur->p + count > cur->end)
|
if (cur->p + count > cur->end)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -464,7 +463,7 @@ static inline int pull_bytes(struct cursor *cur, int count, const unsigned char
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline int parse_str(struct cursor *cur, const char *str) {
|
static inline int parse_str(struct cursor *cur, const char *str) {
|
||||||
unsigned int i;
|
int i;
|
||||||
char c, cs;
|
char c, cs;
|
||||||
unsigned long len;
|
unsigned long len;
|
||||||
|
|
||||||
@@ -486,21 +485,47 @@ static inline int parse_str(struct cursor *cur, const char *str) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_whitespace(char c) {
|
static inline int is_whitespace(int c) {
|
||||||
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
|
return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_underscore(char c) {
|
|
||||||
|
static inline int next_char_is_whitespace(unsigned char *curChar, unsigned char *endChar) {
|
||||||
|
unsigned char * next = curChar + 1;
|
||||||
|
if(next > endChar) return 0;
|
||||||
|
else if(next == endChar) return 1;
|
||||||
|
return is_whitespace(*next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int char_disallowed_at_end_url(char c){
|
||||||
|
return c == '.' || c == ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_final_url_char(unsigned char *curChar, unsigned char *endChar){
|
||||||
|
if(is_whitespace(*curChar)){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else if(next_char_is_whitespace(curChar, endChar)) {
|
||||||
|
// next char is whitespace so this char could be the final char in the url
|
||||||
|
return char_disallowed_at_end_url(*curChar);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// next char isn't whitespace so it can't be a final char
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_underscore(int c) {
|
||||||
return c == '_';
|
return c == '_';
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_utf8_byte(unsigned char c) {
|
static inline int is_utf8_byte(u8 c) {
|
||||||
return c & 0x80;
|
return c & 0x80;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int parse_utf8_char(struct cursor *cursor, unsigned int *code_point, unsigned int *utf8_length)
|
static inline int parse_utf8_char(struct cursor *cursor, unsigned int *code_point, unsigned int *utf8_length)
|
||||||
{
|
{
|
||||||
unsigned char first_byte;
|
u8 first_byte;
|
||||||
if (!parse_byte(cursor, &first_byte))
|
if (!parse_byte(cursor, &first_byte))
|
||||||
return 0; // Not enough data
|
return 0; // Not enough data
|
||||||
|
|
||||||
@@ -525,7 +550,7 @@ static inline int parse_utf8_char(struct cursor *cursor, unsigned int *code_poin
|
|||||||
remaining_bytes = 0;
|
remaining_bytes = 0;
|
||||||
*utf8_length = 1; // Assume 1 byte length for unrecognized UTF-8 characters
|
*utf8_length = 1; // Assume 1 byte length for unrecognized UTF-8 characters
|
||||||
// TODO: We need to gracefully handle unrecognized UTF-8 characters
|
// TODO: We need to gracefully handle unrecognized UTF-8 characters
|
||||||
//printf("Invalid UTF-8 byte: %x\n", *code_point);
|
printf("Invalid UTF-8 byte: %x\n", *code_point);
|
||||||
*code_point = ((first_byte & 0xF0) << 6); // Prevent testing as punctuation
|
*code_point = ((first_byte & 0xF0) << 6); // Prevent testing as punctuation
|
||||||
return 0; // Invalid first byte
|
return 0; // Invalid first byte
|
||||||
}
|
}
|
||||||
@@ -571,7 +596,7 @@ static inline int is_punctuation(unsigned int codepoint) {
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Check for ASCII punctuation
|
// Check for ASCII punctuation
|
||||||
if (codepoint <= 128 && ispunct(codepoint))
|
if (ispunct(codepoint))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// Check for Unicode punctuation exceptions (punctuation allowed in hashtags)
|
// Check for Unicode punctuation exceptions (punctuation allowed in hashtags)
|
||||||
@@ -635,7 +660,8 @@ static inline int consume_until_boundary(struct cursor *cur) {
|
|||||||
if (is_utf8_byte(c)) {
|
if (is_utf8_byte(c)) {
|
||||||
if (!parse_utf8_char(cur, &c, utf8_char_length)) {
|
if (!parse_utf8_char(cur, &c, utf8_char_length)) {
|
||||||
if (!is_right_boundary(c)){
|
if (!is_right_boundary(c)){
|
||||||
return 0;
|
// TODO: We should work towards handling all UTF-8 characters.
|
||||||
|
printf("Invalid UTF-8 code point: %x\n", c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,6 +696,23 @@ static inline int consume_until_whitespace(struct cursor *cur, int or_end) {
|
|||||||
return or_end;
|
return or_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int consume_until_end_url(struct cursor *cur, int or_end) {
|
||||||
|
char c;
|
||||||
|
int consumedAtLeastOne = 0;
|
||||||
|
|
||||||
|
while (cur->p < cur->end) {
|
||||||
|
c = *cur->p;
|
||||||
|
|
||||||
|
if (is_final_url_char(cur->p, cur->end))
|
||||||
|
return consumedAtLeastOne;
|
||||||
|
|
||||||
|
cur->p++;
|
||||||
|
consumedAtLeastOne = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return or_end;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int consume_until_non_alphanumeric(struct cursor *cur, int or_end) {
|
static inline int consume_until_non_alphanumeric(struct cursor *cur, int or_end) {
|
||||||
char c;
|
char c;
|
||||||
int consumedAtLeastOne = 0;
|
int consumedAtLeastOne = 0;
|
||||||
@@ -699,28 +742,5 @@ static inline int cursor_memset(struct cursor *cursor, unsigned char c, int n)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void consume_whitespace_or_punctuation(struct cursor *cur)
|
|
||||||
{
|
|
||||||
while (cur->p < cur->end) {
|
|
||||||
if (!is_right_boundary(*cur->p))
|
|
||||||
return;
|
|
||||||
cur->p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad cursor buffer to n-byte alignment
|
|
||||||
static inline int cursor_align(struct cursor *cur, int bytes) {
|
|
||||||
size_t size = cur->p - cur->start;
|
|
||||||
int pad;
|
|
||||||
|
|
||||||
// pad to n-byte alignment
|
|
||||||
pad = ((size + (bytes-1)) & ~(bytes-1)) - size;
|
|
||||||
|
|
||||||
if (pad > 0 && !cursor_memset(cur, 0, pad))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "damus.h"
|
||||||
#include "bolt11.h"
|
#include "bolt11.h"
|
||||||
#include "amount.h"
|
#include "amount.h"
|
||||||
#include "nostr_bech32.h"
|
#include "nostr_bech32.h"
|
||||||
|
|||||||
393
damus-c/damus.c
Normal file
393
damus-c/damus.c
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
//
|
||||||
|
// damus.c
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-10-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "damus.h"
|
||||||
|
#include "cursor.h"
|
||||||
|
#include "bolt11.h"
|
||||||
|
#include "bech32.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static int parse_digit(struct cursor *cur, int *digit) {
|
||||||
|
int c;
|
||||||
|
if ((c = peek_char(cur, 0)) == -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
c -= '0';
|
||||||
|
|
||||||
|
if (c >= 0 && c <= 9) {
|
||||||
|
*digit = c;
|
||||||
|
cur->p++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int parse_mention_index(struct cursor *cur, struct note_block *block) {
|
||||||
|
int d1, d2, d3, ind;
|
||||||
|
u8 *start = cur->p;
|
||||||
|
|
||||||
|
if (!parse_str(cur, "#["))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!parse_digit(cur, &d1)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ind = d1;
|
||||||
|
|
||||||
|
if (parse_digit(cur, &d2))
|
||||||
|
ind = (d1 * 10) + d2;
|
||||||
|
|
||||||
|
if (parse_digit(cur, &d3))
|
||||||
|
ind = (d1 * 100) + (d2 * 10) + d3;
|
||||||
|
|
||||||
|
if (!parse_char(cur, ']')) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->type = BLOCK_MENTION_INDEX;
|
||||||
|
block->block.mention_index = ind;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_hashtag(struct cursor *cur, struct note_block *block) {
|
||||||
|
int c;
|
||||||
|
u8 *start = cur->p;
|
||||||
|
|
||||||
|
if (!parse_char(cur, '#'))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
c = peek_char(cur, 0);
|
||||||
|
if (c == -1 || is_whitespace(c) || c == '#') {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
consume_until_boundary(cur);
|
||||||
|
|
||||||
|
block->type = BLOCK_HASHTAG;
|
||||||
|
block->block.str.start = (const char*)(start + 1);
|
||||||
|
block->block.str.end = (const char*)cur->p;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_block(struct note_blocks *blocks, struct note_block block)
|
||||||
|
{
|
||||||
|
if (blocks->num_blocks + 1 >= MAX_BLOCKS)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
blocks->blocks[blocks->num_blocks++] = block;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_text_block(struct note_blocks *blocks, const u8 *start, const u8 *end)
|
||||||
|
{
|
||||||
|
struct note_block b;
|
||||||
|
|
||||||
|
if (start == end)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
b.type = BLOCK_TEXT;
|
||||||
|
b.block.str.start = (const char*)start;
|
||||||
|
b.block.str.end = (const char*)end;
|
||||||
|
|
||||||
|
return add_block(blocks, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int consume_url_fragment(struct cursor *cur)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if ((c = peek_char(cur, 0)) < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (c != '#' && c != '?') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur->p++;
|
||||||
|
|
||||||
|
return consume_until_end_url(cur, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int consume_url_path(struct cursor *cur)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if ((c = peek_char(cur, 0)) < 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (c != '/') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cur->p < cur->end) {
|
||||||
|
c = *cur->p;
|
||||||
|
|
||||||
|
if (c == '?' || c == '#' || is_final_url_char(cur->p, cur->end)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur->p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int consume_url_host(struct cursor *cur)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
while (cur->p < cur->end) {
|
||||||
|
c = *cur->p;
|
||||||
|
// TODO: handle IDNs
|
||||||
|
if ((is_alphanumeric(c) || c == '.' || c == '-') && !is_final_url_char(cur->p, cur->end))
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
cur->p++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this means the end of the URL hostname is the end of the buffer and we finished
|
||||||
|
return count != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_url(struct cursor *cur, struct note_block *block) {
|
||||||
|
u8 *start = cur->p;
|
||||||
|
u8 *host;
|
||||||
|
int host_len;
|
||||||
|
struct cursor path_cur;
|
||||||
|
|
||||||
|
if (!parse_str(cur, "http"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (parse_char(cur, 's') || parse_char(cur, 'S')) {
|
||||||
|
if (!parse_str(cur, "://")) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!parse_str(cur, "://")) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure to save the hostname. We will use this to detect damus.io links
|
||||||
|
host = cur->p;
|
||||||
|
|
||||||
|
if (!consume_url_host(cur)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the length of the host string
|
||||||
|
host_len = (int)(cur->p - host);
|
||||||
|
|
||||||
|
// save the current parse state so that we can continue from here when
|
||||||
|
// parsing the bech32 in the damus.io link if we have it
|
||||||
|
copy_cursor(cur, &path_cur);
|
||||||
|
|
||||||
|
// skip leading /
|
||||||
|
cursor_skip(&path_cur, 1);
|
||||||
|
|
||||||
|
if (!consume_url_path(cur)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!consume_url_fragment(cur)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// smart parens
|
||||||
|
if (start - 1 >= 0 &&
|
||||||
|
start < cur->end &&
|
||||||
|
*(start - 1) == '(' &&
|
||||||
|
(cur->p - 1) < cur->end &&
|
||||||
|
*(cur->p - 1) == ')')
|
||||||
|
{
|
||||||
|
cur->p--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the bech32 string pos in case we hit a damus.io link
|
||||||
|
block->block.str.start = (const char *)path_cur.p;
|
||||||
|
|
||||||
|
// if we have a damus link, make it a mention
|
||||||
|
if (host_len == 8
|
||||||
|
&& !strncmp((const char *)host, "damus.io", 8)
|
||||||
|
&& parse_nostr_bech32(&path_cur, &block->block.mention_bech32.bech32))
|
||||||
|
{
|
||||||
|
block->block.str.end = (const char *)path_cur.p;
|
||||||
|
block->type = BLOCK_MENTION_BECH32;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->type = BLOCK_URL;
|
||||||
|
block->block.str.start = (const char *)start;
|
||||||
|
block->block.str.end = (const char *)cur->p;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_invoice(struct cursor *cur, struct note_block *block) {
|
||||||
|
u8 *start, *end;
|
||||||
|
char *fail;
|
||||||
|
struct bolt11 *bolt11;
|
||||||
|
// optional
|
||||||
|
parse_str(cur, "lightning:");
|
||||||
|
|
||||||
|
start = cur->p;
|
||||||
|
|
||||||
|
if (!parse_str(cur, "lnbc"))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!consume_until_whitespace(cur, 1)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = cur->p;
|
||||||
|
|
||||||
|
char str[end - start + 1];
|
||||||
|
str[end - start] = 0;
|
||||||
|
memcpy(str, start, end - start);
|
||||||
|
|
||||||
|
if (!(bolt11 = bolt11_decode(NULL, str, &fail))) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->type = BLOCK_INVOICE;
|
||||||
|
|
||||||
|
block->block.invoice.invstr.start = (const char*)start;
|
||||||
|
block->block.invoice.invstr.end = (const char*)end;
|
||||||
|
block->block.invoice.bolt11 = bolt11;
|
||||||
|
|
||||||
|
cur->p = end;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int parse_mention_bech32(struct cursor *cur, struct note_block *block) {
|
||||||
|
u8 *start = cur->p;
|
||||||
|
|
||||||
|
parse_char(cur, '@');
|
||||||
|
parse_str(cur, "nostr:");
|
||||||
|
|
||||||
|
block->block.str.start = (const char *)cur->p;
|
||||||
|
|
||||||
|
if (!parse_nostr_bech32(cur, &block->block.mention_bech32.bech32)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
block->block.str.end = (const char *)cur->p;
|
||||||
|
|
||||||
|
block->type = BLOCK_MENTION_BECH32;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_text_then_block(struct cursor *cur, struct note_blocks *blocks, struct note_block block, u8 **start, const u8 *pre_mention)
|
||||||
|
{
|
||||||
|
if (!add_text_block(blocks, *start, pre_mention))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*start = (u8*)cur->p;
|
||||||
|
|
||||||
|
if (!add_block(blocks, block))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int damus_parse_content(struct note_blocks *blocks, const char *content) {
|
||||||
|
int cp, c;
|
||||||
|
struct cursor cur;
|
||||||
|
struct note_block block;
|
||||||
|
u8 *start, *pre_mention;
|
||||||
|
|
||||||
|
blocks->words = 0;
|
||||||
|
blocks->num_blocks = 0;
|
||||||
|
make_cursor((u8*)content, (u8*)content + strlen(content), &cur);
|
||||||
|
|
||||||
|
start = cur.p;
|
||||||
|
while (cur.p < cur.end && blocks->num_blocks < MAX_BLOCKS) {
|
||||||
|
cp = peek_char(&cur, -1);
|
||||||
|
c = peek_char(&cur, 0);
|
||||||
|
|
||||||
|
// new word
|
||||||
|
if (is_whitespace(cp) && !is_whitespace(c)) {
|
||||||
|
blocks->words++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre_mention = cur.p;
|
||||||
|
if (cp == -1 || is_left_boundary(cp) || c == '#') {
|
||||||
|
if (c == '#' && (parse_mention_index(&cur, &block) || parse_hashtag(&cur, &block))) {
|
||||||
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
|
return 0;
|
||||||
|
continue;
|
||||||
|
} else if ((c == 'h' || c == 'H') && parse_url(&cur, &block)) {
|
||||||
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
|
return 0;
|
||||||
|
continue;
|
||||||
|
} else if ((c == 'l' || c == 'L') && parse_invoice(&cur, &block)) {
|
||||||
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
|
return 0;
|
||||||
|
continue;
|
||||||
|
} else if ((c == 'n' || c == '@') && parse_mention_bech32(&cur, &block)) {
|
||||||
|
if (!add_text_then_block(&cur, blocks, block, &start, pre_mention))
|
||||||
|
return 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cur.p - start > 0) {
|
||||||
|
if (!add_text_block(blocks, start, cur.p))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blocks_init(struct note_blocks *blocks) {
|
||||||
|
blocks->blocks = malloc(sizeof(struct note_block) * MAX_BLOCKS);
|
||||||
|
blocks->num_blocks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blocks_free(struct note_blocks *blocks) {
|
||||||
|
if (!blocks->blocks) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < blocks->num_blocks; ++i) {
|
||||||
|
if (blocks->blocks[i].type == BLOCK_MENTION_BECH32) {
|
||||||
|
free(blocks->blocks[i].block.mention_bech32.bech32.buffer);
|
||||||
|
blocks->blocks[i].block.mention_bech32.bech32.buffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(blocks->blocks);
|
||||||
|
blocks->num_blocks = 0;
|
||||||
|
}
|
||||||
18
damus-c/damus.h
Normal file
18
damus-c/damus.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//
|
||||||
|
// damus.h
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2022-10-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef damus_h
|
||||||
|
#define damus_h
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "block.h"
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
|
||||||
|
int damus_parse_content(struct note_blocks *blocks, const char *content);
|
||||||
|
|
||||||
|
#endif /* damus_h */
|
||||||
84
damus-c/hex.h
Normal file
84
damus-c/hex.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/* CC0 (Public domain) - see LICENSE file for details */
|
||||||
|
#ifndef CCAN_HEX_H
|
||||||
|
#define CCAN_HEX_H
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hex_decode - Unpack a hex string.
|
||||||
|
* @str: the hexadecimal string
|
||||||
|
* @slen: the length of @str
|
||||||
|
* @buf: the buffer to write the data into
|
||||||
|
* @bufsize: the length of
|
||||||
|
*
|
||||||
|
* Returns false if there are any characters which aren't 0-9, a-f or A-F,
|
||||||
|
* of the string wasn't the right length for @bufsize.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* unsigned char data[20];
|
||||||
|
*
|
||||||
|
* if (!hex_decode(argv[1], strlen(argv[1]), data, 20))
|
||||||
|
* printf("String is malformed!\n");
|
||||||
|
*/
|
||||||
|
bool hex_decode(const char *str, size_t slen, void *buf, size_t bufsize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hex_encode - Create a nul-terminated hex string
|
||||||
|
* @buf: the buffer to read the data from
|
||||||
|
* @bufsize: the length of buf
|
||||||
|
* @dest: the string to fill
|
||||||
|
* @destsize: the max size of the string
|
||||||
|
*
|
||||||
|
* Returns true if the string, including terminator, fit in @destsize;
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* unsigned char buf[] = { 0x1F, 0x2F };
|
||||||
|
* char str[5];
|
||||||
|
*
|
||||||
|
* if (!hex_encode(buf, sizeof(buf), str, sizeof(str)))
|
||||||
|
* abort();
|
||||||
|
*/
|
||||||
|
bool hex_encode(const void *buf, size_t bufsize, char *dest, size_t destsize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hex_str_size - Calculate how big a nul-terminated hex string is
|
||||||
|
* @bytes: bytes of data to represent
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* unsigned char buf[] = { 0x1F, 0x2F };
|
||||||
|
* char str[hex_str_size(sizeof(buf))];
|
||||||
|
*
|
||||||
|
* hex_encode(buf, sizeof(buf), str, sizeof(str));
|
||||||
|
*/
|
||||||
|
static inline size_t hex_str_size(size_t bytes)
|
||||||
|
{
|
||||||
|
return 2 * bytes + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hex_data_size - Calculate how many bytes of data in a hex string
|
||||||
|
* @strlen: the length of the string (with or without NUL)
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* const char str[] = "1F2F";
|
||||||
|
* unsigned char buf[hex_data_size(sizeof(str))];
|
||||||
|
*
|
||||||
|
* hex_decode(str, strlen(str), buf, sizeof(buf));
|
||||||
|
*/
|
||||||
|
static inline size_t hex_data_size(size_t strlen)
|
||||||
|
{
|
||||||
|
return strlen / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char hexchar(unsigned int val)
|
||||||
|
{
|
||||||
|
if (val < 10)
|
||||||
|
return '0' + val;
|
||||||
|
if (val < 16)
|
||||||
|
return 'a' + val - 10;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CCAN_HEX_H */
|
||||||
325
damus-c/nostr_bech32.c
Normal file
325
damus-c/nostr_bech32.c
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
//
|
||||||
|
// nostr_bech32.c
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-04-09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "nostr_bech32.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "endian.h"
|
||||||
|
#include "cursor.h"
|
||||||
|
#include "bech32.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define MAX_TLVS 16
|
||||||
|
|
||||||
|
#define TLV_SPECIAL 0
|
||||||
|
#define TLV_RELAY 1
|
||||||
|
#define TLV_AUTHOR 2
|
||||||
|
#define TLV_KIND 3
|
||||||
|
#define TLV_KNOWN_TLVS 4
|
||||||
|
|
||||||
|
struct nostr_tlv {
|
||||||
|
u8 type;
|
||||||
|
u8 len;
|
||||||
|
const u8 *value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nostr_tlvs {
|
||||||
|
struct nostr_tlv tlvs[MAX_TLVS];
|
||||||
|
int num_tlvs;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parse_nostr_tlv(struct cursor *cur, struct nostr_tlv *tlv) {
|
||||||
|
// get the tlv tag
|
||||||
|
if (!pull_byte(cur, &tlv->type))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// unknown, fail!
|
||||||
|
if (tlv->type >= TLV_KNOWN_TLVS)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// get the length
|
||||||
|
if (!pull_byte(cur, &tlv->len))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// is the reported length greater then our buffer? if so fail
|
||||||
|
if (cur->p + tlv->len > cur->end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tlv->value = cur->p;
|
||||||
|
cur->p += tlv->len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_tlvs(struct cursor *cur, struct nostr_tlvs *tlvs) {
|
||||||
|
int i;
|
||||||
|
tlvs->num_tlvs = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_TLVS; i++) {
|
||||||
|
if (parse_nostr_tlv(cur, &tlvs->tlvs[i])) {
|
||||||
|
tlvs->num_tlvs++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tlvs->num_tlvs == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_tlv(struct nostr_tlvs *tlvs, u8 type, struct nostr_tlv **tlv) {
|
||||||
|
*tlv = NULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < tlvs->num_tlvs; i++) {
|
||||||
|
if (tlvs->tlvs[i].type == type) {
|
||||||
|
*tlv = &tlvs->tlvs[i];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_type(const char *prefix, enum nostr_bech32_type *type) {
|
||||||
|
// Parse type
|
||||||
|
if (strcmp(prefix, "note") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NOTE;
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(prefix, "npub") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NPUB;
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(prefix, "nsec") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NSEC;
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(prefix, "nprofile") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NPROFILE;
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(prefix, "nevent") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NEVENT;
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(prefix, "nrelay") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NRELAY;
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(prefix, "naddr") == 0) {
|
||||||
|
*type = NOSTR_BECH32_NADDR;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_note(struct cursor *cur, struct bech32_note *note) {
|
||||||
|
return pull_bytes(cur, 32, ¬e->event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_npub(struct cursor *cur, struct bech32_npub *npub) {
|
||||||
|
return pull_bytes(cur, 32, &npub->pubkey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_nsec(struct cursor *cur, struct bech32_nsec *nsec) {
|
||||||
|
return pull_bytes(cur, 32, &nsec->nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tlvs_to_relays(struct nostr_tlvs *tlvs, struct relays *relays) {
|
||||||
|
struct nostr_tlv *tlv;
|
||||||
|
struct str_block *str;
|
||||||
|
|
||||||
|
relays->num_relays = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < tlvs->num_tlvs; i++) {
|
||||||
|
tlv = &tlvs->tlvs[i];
|
||||||
|
if (tlv->type != TLV_RELAY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (relays->num_relays + 1 > MAX_RELAYS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
str = &relays->relays[relays->num_relays++];
|
||||||
|
str->start = (const char*)tlv->value;
|
||||||
|
str->end = (const char*)(tlv->value + tlv->len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t decode_tlv_u32(const uint8_t *bytes) {
|
||||||
|
beint32_t *be32_bytes = (beint32_t*)bytes;
|
||||||
|
return be32_to_cpu(*be32_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_nevent(struct cursor *cur, struct bech32_nevent *nevent) {
|
||||||
|
struct nostr_tlvs tlvs;
|
||||||
|
struct nostr_tlv *tlv;
|
||||||
|
|
||||||
|
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (tlv->len != 32)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nevent->event_id = tlv->value;
|
||||||
|
|
||||||
|
if (find_tlv(&tlvs, TLV_AUTHOR, &tlv)) {
|
||||||
|
nevent->pubkey = tlv->value;
|
||||||
|
} else {
|
||||||
|
nevent->pubkey = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(find_tlv(&tlvs, TLV_KIND, &tlv)) {
|
||||||
|
nevent->kind = decode_tlv_u32(tlv->value);
|
||||||
|
nevent->has_kind = true;
|
||||||
|
} else {
|
||||||
|
nevent->has_kind = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlvs_to_relays(&tlvs, &nevent->relays);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_naddr(struct cursor *cur, struct bech32_naddr *naddr) {
|
||||||
|
struct nostr_tlvs tlvs;
|
||||||
|
struct nostr_tlv *tlv;
|
||||||
|
|
||||||
|
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
naddr->identifier.start = (const char*)tlv->value;
|
||||||
|
naddr->identifier.end = (const char*)tlv->value + tlv->len;
|
||||||
|
|
||||||
|
if (!find_tlv(&tlvs, TLV_AUTHOR, &tlv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
naddr->pubkey = tlv->value;
|
||||||
|
|
||||||
|
if(!find_tlv(&tlvs, TLV_KIND, &tlv)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
naddr->kind = decode_tlv_u32(tlv->value);
|
||||||
|
|
||||||
|
return tlvs_to_relays(&tlvs, &naddr->relays);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_nprofile(struct cursor *cur, struct bech32_nprofile *nprofile) {
|
||||||
|
struct nostr_tlvs tlvs;
|
||||||
|
struct nostr_tlv *tlv;
|
||||||
|
|
||||||
|
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (tlv->len != 32)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nprofile->pubkey = tlv->value;
|
||||||
|
|
||||||
|
return tlvs_to_relays(&tlvs, &nprofile->relays);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_nostr_bech32_nrelay(struct cursor *cur, struct bech32_nrelay *nrelay) {
|
||||||
|
struct nostr_tlvs tlvs;
|
||||||
|
struct nostr_tlv *tlv;
|
||||||
|
|
||||||
|
if (!parse_nostr_tlvs(cur, &tlvs))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!find_tlv(&tlvs, TLV_SPECIAL, &tlv))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nrelay->relay.start = (const char*)tlv->value;
|
||||||
|
nrelay->relay.end = (const char*)tlv->value + tlv->len;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj) {
|
||||||
|
u8 *start, *end;
|
||||||
|
|
||||||
|
start = cur->p;
|
||||||
|
|
||||||
|
if (!consume_until_non_alphanumeric(cur, 1)) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = cur->p;
|
||||||
|
|
||||||
|
size_t data_len;
|
||||||
|
size_t input_len = end - start;
|
||||||
|
if (input_len < 10 || input_len > 10000) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj->buffer = malloc(input_len * 2);
|
||||||
|
if (!obj->buffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
u8 data[input_len];
|
||||||
|
char prefix[input_len];
|
||||||
|
|
||||||
|
if (bech32_decode_len(prefix, data, &data_len, (const char*)start, input_len) == BECH32_ENCODING_NONE) {
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj->buflen = 0;
|
||||||
|
if (!bech32_convert_bits(obj->buffer, &obj->buflen, 8, data, data_len, 5, 0)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parse_nostr_bech32_type(prefix, &obj->type)) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cursor bcur;
|
||||||
|
make_cursor(obj->buffer, obj->buffer + obj->buflen, &bcur);
|
||||||
|
|
||||||
|
switch (obj->type) {
|
||||||
|
case NOSTR_BECH32_NOTE:
|
||||||
|
if (!parse_nostr_bech32_note(&bcur, &obj->data.note))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case NOSTR_BECH32_NPUB:
|
||||||
|
if (!parse_nostr_bech32_npub(&bcur, &obj->data.npub))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case NOSTR_BECH32_NSEC:
|
||||||
|
if (!parse_nostr_bech32_nsec(&bcur, &obj->data.nsec))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case NOSTR_BECH32_NEVENT:
|
||||||
|
if (!parse_nostr_bech32_nevent(&bcur, &obj->data.nevent))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case NOSTR_BECH32_NADDR:
|
||||||
|
if (!parse_nostr_bech32_naddr(&bcur, &obj->data.naddr))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case NOSTR_BECH32_NPROFILE:
|
||||||
|
if (!parse_nostr_bech32_nprofile(&bcur, &obj->data.nprofile))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case NOSTR_BECH32_NRELAY:
|
||||||
|
if (!parse_nostr_bech32_nrelay(&bcur, &obj->data.nrelay))
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
free(obj->buffer);
|
||||||
|
cur->p = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
89
damus-c/nostr_bech32.h
Normal file
89
damus-c/nostr_bech32.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
//
|
||||||
|
// nostr_bech32.h
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-04-09.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef nostr_bech32_h
|
||||||
|
#define nostr_bech32_h
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "str_block.h"
|
||||||
|
#include "cursor.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
#define MAX_RELAYS 10
|
||||||
|
|
||||||
|
struct relays {
|
||||||
|
struct str_block relays[MAX_RELAYS];
|
||||||
|
int num_relays;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum nostr_bech32_type {
|
||||||
|
NOSTR_BECH32_NOTE = 1,
|
||||||
|
NOSTR_BECH32_NPUB = 2,
|
||||||
|
NOSTR_BECH32_NPROFILE = 3,
|
||||||
|
NOSTR_BECH32_NEVENT = 4,
|
||||||
|
NOSTR_BECH32_NRELAY = 5,
|
||||||
|
NOSTR_BECH32_NADDR = 6,
|
||||||
|
NOSTR_BECH32_NSEC = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_note {
|
||||||
|
const u8 *event_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_npub {
|
||||||
|
const u8 *pubkey;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_nsec {
|
||||||
|
const u8 *nsec;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_nevent {
|
||||||
|
struct relays relays;
|
||||||
|
const u8 *event_id;
|
||||||
|
const u8 *pubkey; // optional
|
||||||
|
uint32_t kind;
|
||||||
|
bool has_kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_nprofile {
|
||||||
|
struct relays relays;
|
||||||
|
const u8 *pubkey;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_naddr {
|
||||||
|
struct relays relays;
|
||||||
|
struct str_block identifier;
|
||||||
|
const u8 *pubkey;
|
||||||
|
uint32_t kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bech32_nrelay {
|
||||||
|
struct str_block relay;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct nostr_bech32 {
|
||||||
|
enum nostr_bech32_type type;
|
||||||
|
u8 *buffer; // holds strings and tlv stuff
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct bech32_note note;
|
||||||
|
struct bech32_npub npub;
|
||||||
|
struct bech32_nsec nsec;
|
||||||
|
struct bech32_nevent nevent;
|
||||||
|
struct bech32_nprofile nprofile;
|
||||||
|
struct bech32_naddr naddr;
|
||||||
|
struct bech32_nrelay nrelay;
|
||||||
|
} data;
|
||||||
|
} nostr_bech32_t;
|
||||||
|
|
||||||
|
|
||||||
|
int parse_nostr_bech32(struct cursor *cur, struct nostr_bech32 *obj);
|
||||||
|
|
||||||
|
#endif /* nostr_bech32_h */
|
||||||
308
damus-c/sha256.c
Normal file
308
damus-c/sha256.c
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
/* MIT (BSD) license - see LICENSE file for details */
|
||||||
|
/* SHA256 core code translated from the Bitcoin project's C++:
|
||||||
|
*
|
||||||
|
* src/crypto/sha256.cpp commit 417532c8acb93c36c2b6fd052b7c11b6a2906aa2
|
||||||
|
* Copyright (c) 2014 The Bitcoin Core developers
|
||||||
|
* Distributed under the MIT software license, see the accompanying
|
||||||
|
* file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
*/
|
||||||
|
#include "sha256.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
#include "endian.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void invalidate_sha256(struct sha256_ctx *ctx)
|
||||||
|
{
|
||||||
|
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
|
||||||
|
ctx->c.md_len = 0;
|
||||||
|
#else
|
||||||
|
ctx->bytes = (size_t)-1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_sha256(struct sha256_ctx *ctx UNUSED)
|
||||||
|
{
|
||||||
|
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
|
||||||
|
assert(ctx->c.md_len != 0);
|
||||||
|
#else
|
||||||
|
assert(ctx->bytes != (size_t)-1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CCAN_CRYPTO_SHA256_USE_OPENSSL
|
||||||
|
void sha256_init(struct sha256_ctx *ctx)
|
||||||
|
{
|
||||||
|
SHA256_Init(&ctx->c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size)
|
||||||
|
{
|
||||||
|
check_sha256(ctx);
|
||||||
|
SHA256_Update(&ctx->c, p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_done(struct sha256_ctx *ctx, struct sha256 *res)
|
||||||
|
{
|
||||||
|
SHA256_Final(res->u.u8, &ctx->c);
|
||||||
|
invalidate_sha256(ctx);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static uint32_t Ch(uint32_t x, uint32_t y, uint32_t z)
|
||||||
|
{
|
||||||
|
return z ^ (x & (y ^ z));
|
||||||
|
}
|
||||||
|
static uint32_t Maj(uint32_t x, uint32_t y, uint32_t z)
|
||||||
|
{
|
||||||
|
return (x & y) | (z & (x | y));
|
||||||
|
}
|
||||||
|
static uint32_t Sigma0(uint32_t x)
|
||||||
|
{
|
||||||
|
return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10);
|
||||||
|
}
|
||||||
|
static uint32_t Sigma1(uint32_t x)
|
||||||
|
{
|
||||||
|
return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7);
|
||||||
|
}
|
||||||
|
static uint32_t sigma0(uint32_t x)
|
||||||
|
{
|
||||||
|
return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3);
|
||||||
|
}
|
||||||
|
static uint32_t sigma1(uint32_t x)
|
||||||
|
{
|
||||||
|
return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** One round of SHA-256. */
|
||||||
|
static void Round(uint32_t a, uint32_t b, uint32_t c, uint32_t *d, uint32_t e, uint32_t f, uint32_t g, uint32_t *h, uint32_t k, uint32_t w)
|
||||||
|
{
|
||||||
|
uint32_t t1 = *h + Sigma1(e) + Ch(e, f, g) + k + w;
|
||||||
|
uint32_t t2 = Sigma0(a) + Maj(a, b, c);
|
||||||
|
*d += t1;
|
||||||
|
*h = t1 + t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Perform one SHA-256 transformation, processing a 64-byte chunk. */
|
||||||
|
static void Transform(uint32_t *s, const uint32_t *chunk)
|
||||||
|
{
|
||||||
|
uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
|
||||||
|
uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
|
||||||
|
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0x428a2f98, w0 = be32_to_cpu(chunk[0]));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0x71374491, w1 = be32_to_cpu(chunk[1]));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0xb5c0fbcf, w2 = be32_to_cpu(chunk[2]));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0xe9b5dba5, w3 = be32_to_cpu(chunk[3]));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0x3956c25b, w4 = be32_to_cpu(chunk[4]));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0x59f111f1, w5 = be32_to_cpu(chunk[5]));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0x923f82a4, w6 = be32_to_cpu(chunk[6]));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0xab1c5ed5, w7 = be32_to_cpu(chunk[7]));
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0xd807aa98, w8 = be32_to_cpu(chunk[8]));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0x12835b01, w9 = be32_to_cpu(chunk[9]));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0x243185be, w10 = be32_to_cpu(chunk[10]));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0x550c7dc3, w11 = be32_to_cpu(chunk[11]));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0x72be5d74, w12 = be32_to_cpu(chunk[12]));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0x80deb1fe, w13 = be32_to_cpu(chunk[13]));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0x9bdc06a7, w14 = be32_to_cpu(chunk[14]));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0xc19bf174, w15 = be32_to_cpu(chunk[15]));
|
||||||
|
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8));
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
|
||||||
|
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8));
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
|
||||||
|
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8));
|
||||||
|
Round(a, b, c, &d, e, f, g, &h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9));
|
||||||
|
Round(h, a, b, &c, d, e, f, &g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10));
|
||||||
|
Round(g, h, a, &b, c, d, e, &f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11));
|
||||||
|
Round(f, g, h, &a, b, c, d, &e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12));
|
||||||
|
Round(e, f, g, &h, a, b, c, &d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13));
|
||||||
|
Round(d, e, f, &g, h, a, b, &c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14));
|
||||||
|
Round(c, d, e, &f, g, h, a, &b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
|
||||||
|
Round(b, c, d, &e, f, g, h, &a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));
|
||||||
|
|
||||||
|
s[0] += a;
|
||||||
|
s[1] += b;
|
||||||
|
s[2] += c;
|
||||||
|
s[3] += d;
|
||||||
|
s[4] += e;
|
||||||
|
s[5] += f;
|
||||||
|
s[6] += g;
|
||||||
|
s[7] += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool alignment_ok(const void *p UNUSED, size_t n UNUSED)
|
||||||
|
{
|
||||||
|
#if HAVE_UNALIGNED_ACCESS
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return ((size_t)p % n == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add(struct sha256_ctx *ctx, const void *p, size_t len)
|
||||||
|
{
|
||||||
|
const unsigned char *data = p;
|
||||||
|
size_t bufsize = ctx->bytes % 64;
|
||||||
|
|
||||||
|
if (bufsize + len >= 64) {
|
||||||
|
/* Fill the buffer, and process it. */
|
||||||
|
memcpy(ctx->buf.u8 + bufsize, data, 64 - bufsize);
|
||||||
|
ctx->bytes += 64 - bufsize;
|
||||||
|
data += 64 - bufsize;
|
||||||
|
len -= 64 - bufsize;
|
||||||
|
Transform(ctx->s, ctx->buf.u32);
|
||||||
|
bufsize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (len >= 64) {
|
||||||
|
/* Process full chunks directly from the source. */
|
||||||
|
if (alignment_ok(data, sizeof(uint32_t)))
|
||||||
|
Transform(ctx->s, (const uint32_t *)data);
|
||||||
|
else {
|
||||||
|
memcpy(ctx->buf.u8, data, sizeof(ctx->buf));
|
||||||
|
Transform(ctx->s, ctx->buf.u32);
|
||||||
|
}
|
||||||
|
ctx->bytes += 64;
|
||||||
|
data += 64;
|
||||||
|
len -= 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
/* Fill the buffer with what remains. */
|
||||||
|
memcpy(ctx->buf.u8 + bufsize, data, len);
|
||||||
|
ctx->bytes += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_init(struct sha256_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct sha256_ctx init = SHA256_INIT;
|
||||||
|
*ctx = init;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_update(struct sha256_ctx *ctx, const void *p, size_t size)
|
||||||
|
{
|
||||||
|
check_sha256(ctx);
|
||||||
|
add(ctx, p, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_done(struct sha256_ctx *ctx, struct sha256 *res)
|
||||||
|
{
|
||||||
|
static const unsigned char pad[64] = {0x80};
|
||||||
|
uint64_t sizedesc;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
sizedesc = cpu_to_be64((uint64_t)ctx->bytes << 3);
|
||||||
|
/* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */
|
||||||
|
add(ctx, pad, 1 + ((128 - 8 - (ctx->bytes % 64) - 1) % 64));
|
||||||
|
/* Add number of bits of data (big endian) */
|
||||||
|
add(ctx, &sizedesc, 8);
|
||||||
|
for (i = 0; i < sizeof(ctx->s) / sizeof(ctx->s[0]); i++)
|
||||||
|
res->u.u32[i] = cpu_to_be32(ctx->s[i]);
|
||||||
|
invalidate_sha256(ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void sha256(struct sha256 *sha, const void *p, size_t size)
|
||||||
|
{
|
||||||
|
struct sha256_ctx ctx;
|
||||||
|
|
||||||
|
sha256_init(&ctx);
|
||||||
|
sha256_update(&ctx, p, size);
|
||||||
|
sha256_done(&ctx, sha);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_u8(struct sha256_ctx *ctx, uint8_t v)
|
||||||
|
{
|
||||||
|
sha256_update(ctx, &v, sizeof(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_u16(struct sha256_ctx *ctx, uint16_t v)
|
||||||
|
{
|
||||||
|
sha256_update(ctx, &v, sizeof(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_u32(struct sha256_ctx *ctx, uint32_t v)
|
||||||
|
{
|
||||||
|
sha256_update(ctx, &v, sizeof(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_u64(struct sha256_ctx *ctx, uint64_t v)
|
||||||
|
{
|
||||||
|
sha256_update(ctx, &v, sizeof(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add as little-endian */
|
||||||
|
void sha256_le16(struct sha256_ctx *ctx, uint16_t v)
|
||||||
|
{
|
||||||
|
leint16_t lev = cpu_to_le16(v);
|
||||||
|
sha256_update(ctx, &lev, sizeof(lev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_le32(struct sha256_ctx *ctx, uint32_t v)
|
||||||
|
{
|
||||||
|
leint32_t lev = cpu_to_le32(v);
|
||||||
|
sha256_update(ctx, &lev, sizeof(lev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_le64(struct sha256_ctx *ctx, uint64_t v)
|
||||||
|
{
|
||||||
|
leint64_t lev = cpu_to_le64(v);
|
||||||
|
sha256_update(ctx, &lev, sizeof(lev));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add as big-endian */
|
||||||
|
void sha256_be16(struct sha256_ctx *ctx, uint16_t v)
|
||||||
|
{
|
||||||
|
beint16_t bev = cpu_to_be16(v);
|
||||||
|
sha256_update(ctx, &bev, sizeof(bev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_be32(struct sha256_ctx *ctx, uint32_t v)
|
||||||
|
{
|
||||||
|
beint32_t bev = cpu_to_be32(v);
|
||||||
|
sha256_update(ctx, &bev, sizeof(bev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void sha256_be64(struct sha256_ctx *ctx, uint64_t v)
|
||||||
|
{
|
||||||
|
beint64_t bev = cpu_to_be64(v);
|
||||||
|
sha256_update(ctx, &bev, sizeof(bev));
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ struct sha256 {
|
|||||||
* sha256 - return sha256 of an object.
|
* sha256 - return sha256 of an object.
|
||||||
* @sha256: the sha256 to fill in
|
* @sha256: the sha256 to fill in
|
||||||
* @p: pointer to memory,
|
* @p: pointer to memory,
|
||||||
* @size: the number of bytes pointed to by @p
|
* @size: the number of bytes pointed to by
|
||||||
*
|
*
|
||||||
* The bytes pointed to by @p is SHA256 hashed into @sha256. This is
|
* The bytes pointed to by @p is SHA256 hashed into @sha256. This is
|
||||||
* equivalent to sha256_init(), sha256_update() then sha256_done().
|
* equivalent to sha256_init(), sha256_update() then sha256_done().
|
||||||
@@ -112,7 +112,7 @@ void sha256_init(struct sha256_ctx *ctx);
|
|||||||
* sha256_update - include some memory in the hash.
|
* sha256_update - include some memory in the hash.
|
||||||
* @ctx: the sha256_ctx to use
|
* @ctx: the sha256_ctx to use
|
||||||
* @p: pointer to memory,
|
* @p: pointer to memory,
|
||||||
* @size: the number of bytes pointed to by @p
|
* @size: the number of bytes pointed to by
|
||||||
*
|
*
|
||||||
* You can call this multiple times to hash more data, before calling
|
* You can call this multiple times to hash more data, before calling
|
||||||
* sha256_done().
|
* sha256_done().
|
||||||
@@ -8,4 +8,9 @@
|
|||||||
#ifndef str_block_h
|
#ifndef str_block_h
|
||||||
#define str_block_h
|
#define str_block_h
|
||||||
|
|
||||||
|
typedef struct str_block {
|
||||||
|
const char *start;
|
||||||
|
const char *end;
|
||||||
|
} str_block_t;
|
||||||
|
|
||||||
#endif /* str_block_h */
|
#endif /* str_block_h */
|
||||||
14
damus-c/typedefs.h
Normal file
14
damus-c/typedefs.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
#ifndef PROTOVERSE_TYPEDEFS_H
|
||||||
|
#define PROTOVERSE_TYPEDEFS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef int64_t s64;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* PROTOVERSE_TYPEDEFS_H */
|
||||||
@@ -1179,7 +1179,7 @@ static INLINE int parse_i64(struct cursor *read, uint64_t *val)
|
|||||||
shift = 0;
|
shift = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!cursor_pull_byte(read, &byte))
|
if (!pull_byte(read, &byte))
|
||||||
return 0;
|
return 0;
|
||||||
*val |= (byte & 0x7FULL) << shift;
|
*val |= (byte & 0x7FULL) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
@@ -1199,7 +1199,7 @@ static INLINE int uleb128_read(struct cursor *read, unsigned int *val)
|
|||||||
*val = 0;
|
*val = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!cursor_pull_byte(read, &byte))
|
if (!pull_byte(read, &byte))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
*val |= (0x7F & byte) << shift;
|
*val |= (0x7F & byte) << shift;
|
||||||
@@ -1222,7 +1222,7 @@ static INLINE int sleb128_read(struct cursor *read, signed int *val)
|
|||||||
shift = 0;
|
shift = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!cursor_pull_byte(read, &byte))
|
if (!pull_byte(read, &byte))
|
||||||
return 0;
|
return 0;
|
||||||
*val |= ((byte & 0x7F) << shift);
|
*val |= ((byte & 0x7F) << shift);
|
||||||
shift += 7;
|
shift += 7;
|
||||||
@@ -1241,21 +1241,21 @@ static INLINE int uleb128_read(struct cursor *read, unsigned int *val)
|
|||||||
unsigned char p[6] = {0};
|
unsigned char p[6] = {0};
|
||||||
*val = 0;
|
*val = 0;
|
||||||
|
|
||||||
if (cursor_pull_byte(read, &p[0]) && (p[0] & 0x80) == 0) {
|
if (pull_byte(read, &p[0]) && (p[0] & 0x80) == 0) {
|
||||||
*val = LEB128_1(unsigned int);
|
*val = LEB128_1(unsigned int);
|
||||||
if (p[0] == 0x7F)
|
if (p[0] == 0x7F)
|
||||||
assert((int)*val == -1);
|
assert((int)*val == -1);
|
||||||
return 1;
|
return 1;
|
||||||
} else if (cursor_pull_byte(read, &p[1]) && (p[1] & 0x80) == 0) {
|
} else if (pull_byte(read, &p[1]) && (p[1] & 0x80) == 0) {
|
||||||
*val = LEB128_2(unsigned int);
|
*val = LEB128_2(unsigned int);
|
||||||
return 2;
|
return 2;
|
||||||
} else if (cursor_pull_byte(read, &p[2]) && (p[2] & 0x80) == 0) {
|
} else if (pull_byte(read, &p[2]) && (p[2] & 0x80) == 0) {
|
||||||
*val = LEB128_3(unsigned int);
|
*val = LEB128_3(unsigned int);
|
||||||
return 3;
|
return 3;
|
||||||
} else if (cursor_pull_byte(read, &p[3]) && (p[3] & 0x80) == 0) {
|
} else if (pull_byte(read, &p[3]) && (p[3] & 0x80) == 0) {
|
||||||
*val = LEB128_4(unsigned int);
|
*val = LEB128_4(unsigned int);
|
||||||
return 4;
|
return 4;
|
||||||
} else if (cursor_pull_byte(read, &p[4]) && (p[4] & 0x80) == 0) {
|
} else if (pull_byte(read, &p[4]) && (p[4] & 0x80) == 0) {
|
||||||
if (!(p[4] & 0xF0)) {
|
if (!(p[4] & 0xF0)) {
|
||||||
*val = LEB128_5(unsigned int);
|
*val = LEB128_5(unsigned int);
|
||||||
return 5;
|
return 5;
|
||||||
@@ -1296,7 +1296,7 @@ static int parse_section_tag(struct cursor *cur, enum section_tag *section)
|
|||||||
|
|
||||||
start = cur->p;
|
start = cur->p;
|
||||||
|
|
||||||
if (!cursor_pull_byte(cur, &byte)) {
|
if (!pull_byte(cur, &byte)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1315,7 +1315,7 @@ static int parse_valtype(struct wasm_parser *p, enum valtype *valtype)
|
|||||||
|
|
||||||
start = p->cur.p;
|
start = p->cur.p;
|
||||||
|
|
||||||
if (unlikely(!cursor_pull_byte(&p->cur, (unsigned char*)valtype))) {
|
if (unlikely(!pull_byte(&p->cur, (unsigned char*)valtype))) {
|
||||||
return parse_err(p, "valtype tag oob");
|
return parse_err(p, "valtype tag oob");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1416,7 +1416,7 @@ static int parse_export_desc(struct wasm_parser *p, enum exportdesc *desc)
|
|||||||
{
|
{
|
||||||
unsigned char byte;
|
unsigned char byte;
|
||||||
|
|
||||||
if (!cursor_pull_byte(&p->cur, &byte)) {
|
if (!pull_byte(&p->cur, &byte)) {
|
||||||
parse_err(p, "export desc byte eof");
|
parse_err(p, "export desc byte eof");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1523,7 +1523,7 @@ static int parse_name_subsection(struct wasm_parser *p, struct namesec *sec, u32
|
|||||||
u8 tag;
|
u8 tag;
|
||||||
u8 *start = p->cur.p;
|
u8 *start = p->cur.p;
|
||||||
|
|
||||||
if (!cursor_pull_byte(&p->cur, &tag))
|
if (!pull_byte(&p->cur, &tag))
|
||||||
return parse_err(p, "name subsection tag oob?");
|
return parse_err(p, "name subsection tag oob?");
|
||||||
|
|
||||||
if (!is_valid_name_subsection(tag))
|
if (!is_valid_name_subsection(tag))
|
||||||
@@ -1676,7 +1676,7 @@ static int parse_reftype(struct wasm_parser *p, enum reftype *reftype)
|
|||||||
{
|
{
|
||||||
u8 tag;
|
u8 tag;
|
||||||
|
|
||||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
if (!pull_byte(&p->cur, &tag)) {
|
||||||
parse_err(p, "reftype");
|
parse_err(p, "reftype");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1720,7 +1720,7 @@ static int parse_export_section(struct wasm_parser *p,
|
|||||||
static int parse_limits(struct wasm_parser *p, struct limits *limits)
|
static int parse_limits(struct wasm_parser *p, struct limits *limits)
|
||||||
{
|
{
|
||||||
unsigned char tag;
|
unsigned char tag;
|
||||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
if (!pull_byte(&p->cur, &tag)) {
|
||||||
return parse_err(p, "oob");
|
return parse_err(p, "oob");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1803,7 +1803,7 @@ static void print_code(u8 *code, int code_len)
|
|||||||
make_cursor(code, code + code_len, &c);
|
make_cursor(code, code + code_len, &c);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!cursor_pull_byte(&c, &tag)) {
|
if (!pull_byte(&c, &tag)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2169,7 +2169,7 @@ static int parse_const_expr(struct expr_parser *p, struct expr *expr)
|
|||||||
expr->code = p->code->p;
|
expr->code = p->code->p;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (unlikely(!cursor_pull_byte(p->code, &tag))) {
|
if (unlikely(!pull_byte(p->code, &tag))) {
|
||||||
return note_error(p->errs, p->code, "oob");
|
return note_error(p->errs, p->code, "oob");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2332,7 +2332,7 @@ static int parse_instrs_until_at(struct expr_parser *p, u8 stop_instr,
|
|||||||
p->code->p - p->code->start,
|
p->code->p - p->code->start,
|
||||||
dbg_inst, instr_name(stop_instr));
|
dbg_inst, instr_name(stop_instr));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (!cursor_pull_byte(p->code, &tag))
|
if (!pull_byte(p->code, &tag))
|
||||||
return note_error(p->errs, p->code, "oob");
|
return note_error(p->errs, p->code, "oob");
|
||||||
|
|
||||||
if ((tag != i_if && tag == stop_instr) ||
|
if ((tag != i_if && tag == stop_instr) ||
|
||||||
@@ -2413,7 +2413,7 @@ static int parse_element(struct wasm_parser *p, struct elem *elem)
|
|||||||
|
|
||||||
make_expr_parser(&p->errs, &p->cur, &expr_parser);
|
make_expr_parser(&p->errs, &p->cur, &expr_parser);
|
||||||
|
|
||||||
if (!cursor_pull_byte(&p->cur, &tag))
|
if (!pull_byte(&p->cur, &tag))
|
||||||
return parse_err(p, "tag");
|
return parse_err(p, "tag");
|
||||||
|
|
||||||
if (tag > 7)
|
if (tag > 7)
|
||||||
@@ -2545,7 +2545,7 @@ static int parse_wdata(struct wasm_parser *p, struct wdata *data)
|
|||||||
struct expr_parser parser;
|
struct expr_parser parser;
|
||||||
u8 tag;
|
u8 tag;
|
||||||
|
|
||||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
if (!pull_byte(&p->cur, &tag)) {
|
||||||
return parse_err(p, "tag");
|
return parse_err(p, "tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2700,7 +2700,7 @@ static int parse_importdesc(struct wasm_parser *p, struct importdesc *desc)
|
|||||||
{
|
{
|
||||||
u8 tag;
|
u8 tag;
|
||||||
|
|
||||||
if (!cursor_pull_byte(&p->cur, &tag)) {
|
if (!pull_byte(&p->cur, &tag)) {
|
||||||
parse_err(p, "oom");
|
parse_err(p, "oom");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -4134,7 +4134,7 @@ static int parse_blocktype(struct cursor *cur, struct errors *errs, struct block
|
|||||||
{
|
{
|
||||||
unsigned char byte;
|
unsigned char byte;
|
||||||
|
|
||||||
if (unlikely(!cursor_pull_byte(cur, &byte))) {
|
if (unlikely(!pull_byte(cur, &byte))) {
|
||||||
return note_error(errs, cur, "parse_blocktype: oob\n");
|
return note_error(errs, cur, "parse_blocktype: oob\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4656,7 +4656,7 @@ static int parse_bulk_op(struct cursor *code, struct errors *errs,
|
|||||||
{
|
{
|
||||||
u8 tag;
|
u8 tag;
|
||||||
|
|
||||||
if (unlikely(!cursor_pull_byte(code, &tag)))
|
if (unlikely(!pull_byte(code, &tag)))
|
||||||
return note_error(errs, code, "oob");
|
return note_error(errs, code, "oob");
|
||||||
|
|
||||||
if (unlikely(tag < 10 || tag > 17))
|
if (unlikely(tag < 10 || tag > 17))
|
||||||
@@ -6552,7 +6552,7 @@ static INLINE int interp_parse_instr(struct wasm_interp *interp,
|
|||||||
{
|
{
|
||||||
u8 tag;
|
u8 tag;
|
||||||
|
|
||||||
if (unlikely(!cursor_pull_byte(code, &tag))) {
|
if (unlikely(!pull_byte(code, &tag))) {
|
||||||
return interp_error(interp, "no more instrs to pull");
|
return interp_error(interp, "no more instrs to pull");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ static const unsigned char WASM_MAGIC[] = {0,'a','s','m'};
|
|||||||
#define interp_error(p, fmt, ...) note_error(&((p)->errors), interp_codeptr(p), fmt, ##__VA_ARGS__)
|
#define interp_error(p, fmt, ...) note_error(&((p)->errors), interp_codeptr(p), fmt, ##__VA_ARGS__)
|
||||||
#define parse_err(p, fmt, ...) note_error(&((p)->errs), &(p)->cur, fmt, ##__VA_ARGS__)
|
#define parse_err(p, fmt, ...) note_error(&((p)->errs), &(p)->cur, fmt, ##__VA_ARGS__)
|
||||||
|
|
||||||
#include "short_types.h"
|
|
||||||
|
|
||||||
enum valtype {
|
enum valtype {
|
||||||
val_i32 = 0x7F,
|
val_i32 = 0x7F,
|
||||||
val_i64 = 0x7E,
|
val_i64 = 0x7E,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "1fc7e0b44329ba72cd285eeb022b5b92582cd01586b920d243cb0485c2e69dcc",
|
"originHash" : "06318d35ee2e6bd681b95591e67da33a9461b48a3c652e58bd9d1a6f0d82bdac",
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "codescanner",
|
"identity" : "codescanner",
|
||||||
@@ -35,15 +35,6 @@
|
|||||||
"version" : "0.2.0"
|
"version" : "0.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "faviconfinder",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/will-lumley/FaviconFinder.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "9279f4371f4877ca302ba3bf1015f3f58ae4a56c",
|
|
||||||
"version" : "5.1.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "gsplayer",
|
"identity" : "gsplayer",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
@@ -114,15 +105,6 @@
|
|||||||
"version" : "0.1.2"
|
"version" : "0.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"identity" : "swiftsoup",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/scinfu/SwiftSoup.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "bba848db50462894e7fc0891d018dfecad4ef11e",
|
|
||||||
"version" : "2.8.7"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"identity" : "swiftycrop",
|
"identity" : "swiftycrop",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -55,7 +55,6 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableAddressSanitizer = "YES"
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "blink.png",
|
"filename" : "bbw.jpg",
|
||||||
"idiom" : "universal"
|
"idiom" : "universal"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
BIN
damus/Assets.xcassets/Logos/bbw.imageset/bbw.jpg
vendored
Normal file
BIN
damus/Assets.xcassets/Logos/bbw.imageset/bbw.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
damus/Assets.xcassets/Logos/blink.imageset/blink.png
vendored
BIN
damus/Assets.xcassets/Logos/blink.imageset/blink.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 115 KiB |
15
damus/Components/Gradients/AlbyGradient.swift
Normal file
15
damus/Components/Gradients/AlbyGradient.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// AlbyGradient.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by William Casarin on 2023-05-09.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
fileprivate let alby_grad_c1 = hex_col(r: 226, g: 168, b: 122)
|
||||||
|
fileprivate let alby_grad_c2 = hex_col(r: 249, g: 223, b: 127)
|
||||||
|
fileprivate let alby_grad = [alby_grad_c2, alby_grad_c1]
|
||||||
|
|
||||||
|
let AlbyGradient: LinearGradient =
|
||||||
|
LinearGradient(colors: alby_grad, startPoint: .bottomLeading, endPoint: .topTrailing)
|
||||||
@@ -162,7 +162,6 @@ class CarouselModel: ObservableObject {
|
|||||||
// Upon updating information, update the carousel fill size if the size for the current url has changed
|
// Upon updating information, update the carousel fill size if the size for the current url has changed
|
||||||
if oldValue[current_url] != media_size_information[current_url] {
|
if oldValue[current_url] != media_size_information[current_url] {
|
||||||
self.refresh_current_item_fill()
|
self.refresh_current_item_fill()
|
||||||
self.refresh_first_item_height()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,13 +186,6 @@ class CarouselModel: ObservableObject {
|
|||||||
/// and is automatically updated upon changes to these properties.
|
/// and is automatically updated upon changes to these properties.
|
||||||
@Published private(set) var current_item_fill: ImageFill?
|
@Published private(set) var current_item_fill: ImageFill?
|
||||||
|
|
||||||
/// Holds the ideal fill dimensions for the first item in the carousel.
|
|
||||||
/// This is used to maintain a consistent height for the carousel when swiping between images.
|
|
||||||
/// **Usage note:** This property is automatically updated when other properties are set, and should not be set directly.
|
|
||||||
/// **Implementation note:** This property ensures the carousel maintains a consistent height based on the first image,
|
|
||||||
/// preventing the UI from "jumping" when swiping between images of different aspect ratios.
|
|
||||||
@Published private(set) var first_image_fill: ImageFill?
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: Initialization and de-initialization
|
// MARK: Initialization and de-initialization
|
||||||
|
|
||||||
@@ -215,7 +207,6 @@ class CarouselModel: ObservableObject {
|
|||||||
self.observe_video_sizes()
|
self.observe_video_sizes()
|
||||||
Task {
|
Task {
|
||||||
self.refresh_current_item_fill()
|
self.refresh_current_item_fill()
|
||||||
self.refresh_first_item_height()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,17 +241,10 @@ class CarouselModel: ObservableObject {
|
|||||||
/// **Usage note:** This is private, do not call this directly from outside the class.
|
/// **Usage note:** This is private, do not call this directly from outside the class.
|
||||||
/// **Implementation note:** This should be called using `didSet` observers on properties that affect the fill
|
/// **Implementation note:** This should be called using `didSet` observers on properties that affect the fill
|
||||||
private func refresh_current_item_fill() {
|
private func refresh_current_item_fill() {
|
||||||
self.current_item_fill = self.compute_item_fill(url: current_url)
|
if let current_url,
|
||||||
}
|
let item_size = self.media_size_information[current_url],
|
||||||
|
|
||||||
/// Computes the image fill properties for a given URL without side effects.
|
|
||||||
/// This is a pure function that calculates the appropriate fill dimensions based on image size and container constraints.
|
|
||||||
/// **Usage note:** This is a helper method used by both `refresh_current_item_fill` and `refresh_first_item_height`.
|
|
||||||
private func compute_item_fill(url: URL?) -> ImageFill? {
|
|
||||||
if let url,
|
|
||||||
let item_size = self.media_size_information[url],
|
|
||||||
let geo_size {
|
let geo_size {
|
||||||
return ImageFill.calculate_image_fill(
|
self.current_item_fill = ImageFill.calculate_image_fill(
|
||||||
geo_size: geo_size,
|
geo_size: geo_size,
|
||||||
img_size: item_size,
|
img_size: item_size,
|
||||||
maxHeight: self.max_height,
|
maxHeight: self.max_height,
|
||||||
@@ -268,26 +252,9 @@ class CarouselModel: ObservableObject {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil // Not enough information to compute the proper fill. Default to nil
|
self.current_item_fill = nil // Not enough information to compute the proper fill. Default to nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function refreshes the first item height based on the current state of the model
|
|
||||||
/// **Usage note:** This is private, do not call this directly from outside the class.
|
|
||||||
/// **Implementation note:** This should be called using `didSet` observers on properties that affect the height.
|
|
||||||
/// When the first image dimensions change, this ensures the carousel maintains consistent dimensions.
|
|
||||||
private func refresh_first_item_height() {
|
|
||||||
self.first_image_fill = self.compute_first_item_fill()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes the first item fill with no side-effects.
|
|
||||||
/// **Usage note:** Not to be used outside the class. Use the `first_image_fill` property instead.
|
|
||||||
/// **Implementation note:** This retrieves the first URL from the carousel and computes its fill properties
|
|
||||||
/// to establish a consistent height for the entire carousel.
|
|
||||||
private func compute_first_item_fill() -> ImageFill? {
|
|
||||||
guard let first_url = urls[safe: 0] else { return nil }
|
|
||||||
return self.compute_item_fill(url: first_url.url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Image Carousel
|
// MARK: - Image Carousel
|
||||||
@@ -319,15 +286,13 @@ struct ImageCarousel<Content: View>: View {
|
|||||||
self.content = content
|
self.content = content
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if the image should fill its container.
|
var filling: Bool {
|
||||||
/// Always returns true to ensure images consistently fill the width of the container.
|
model.current_item_fill?.filling == true
|
||||||
/// This simplifies the layout behavior and prevents inconsistent sizing between carousel items.
|
}
|
||||||
var filling: Bool { true }
|
|
||||||
|
|
||||||
var height: CGFloat {
|
var height: CGFloat {
|
||||||
// Use the first image height (to prevent height from jumping when swiping), then default to the default fill height
|
// Use the calculated fill height if available, otherwise use the default fill height
|
||||||
// This prioritization ensures consistent carousel height regardless of which image is currently displayed
|
model.current_item_fill?.height ?? model.default_fill_height
|
||||||
model.first_image_fill?.height ?? model.default_fill_height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Placeholder(url: URL, geo_size: CGSize, num_urls: Int) -> some View {
|
func Placeholder(url: URL, geo_size: CGSize, num_urls: Int) -> some View {
|
||||||
@@ -411,7 +376,6 @@ struct ImageCarousel<Content: View>: View {
|
|||||||
}
|
}
|
||||||
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
|
||||||
.frame(height: height)
|
.frame(height: height)
|
||||||
.clipped() // Prevents content from overflowing the frame, ensuring clean edges in the carousel
|
|
||||||
.onChange(of: model.selectedIndex) { value in
|
.onChange(of: model.selectedIndex) { value in
|
||||||
model.selectedIndex = value
|
model.selectedIndex = value
|
||||||
}
|
}
|
||||||
@@ -118,8 +118,7 @@ func getUrlToOpen(invoice: String, with wallet: Wallet.Model) throws(OpenWalletE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let test_invoice = Invoice(description: .description("this is a description"), amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, payment_hash: Data(), created_at: 1666139119)
|
||||||
let test_invoice = Invoice(description: .description("this is a description"), amount: .specific(10000), string: "lnbc100n1p357sl0sp5t9n56wdztun39lgdqlr30xqwksg3k69q4q2rkr52aplujw0esn0qpp5mrqgljk62z20q4nvgr6lzcyn6fhylzccwdvu4k77apg3zmrkujjqdpzw35xjueqd9ejqcfqv3jhxcmjd9c8g6t0dcxqyjw5qcqpjrzjqt56h4gvp5yx36u2uzqa6qwcsk3e2duunfxppzj9vhypc3wfe2wswz607uqq3xqqqsqqqqqqqqqqqlqqyg9qyysgqagx5h20aeulj3gdwx3kxs8u9f4mcakdkwuakasamm9562ffyr9en8yg20lg0ygnr9zpwp68524kmda0t5xp2wytex35pu8hapyjajxqpsql29r", expiry: 604800, created_at: 1666139119)
|
|
||||||
|
|
||||||
struct InvoiceView_Previews: PreviewProvider {
|
struct InvoiceView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
@@ -29,7 +29,7 @@ struct InvoicesView: View {
|
|||||||
|
|
||||||
struct InvoicesView_Previews: PreviewProvider {
|
struct InvoicesView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
InvoicesView(our_pubkey: test_note.pubkey, invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, created_at: 1000000)], settings: test_damus_state.settings)
|
InvoicesView(our_pubkey: test_note.pubkey, invoices: [Invoice.init(description: .description("description"), amount: .specific(10000), string: "invstr", expiry: 100000, payment_hash: Data(), created_at: 1000000)], settings: test_damus_state.settings)
|
||||||
.frame(width: 300)
|
.frame(width: 300)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,27 +5,27 @@
|
|||||||
// Created by William Casarin on 2023-01-11.
|
// Created by William Casarin on 2023-01-11.
|
||||||
//
|
//
|
||||||
|
|
||||||
import FaviconFinder
|
|
||||||
import Kingfisher
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct NIP05Badge: View {
|
struct NIP05Badge: View {
|
||||||
let nip05: NIP05
|
let nip05: NIP05
|
||||||
let pubkey: Pubkey
|
let pubkey: Pubkey
|
||||||
let damus_state: DamusState
|
let contacts: Contacts
|
||||||
let show_domain: Bool
|
let show_domain: Bool
|
||||||
let nip05_domain_favicon: FaviconURL?
|
let profiles: Profiles
|
||||||
|
|
||||||
init(nip05: NIP05, pubkey: Pubkey, damus_state: DamusState, show_domain: Bool, nip05_domain_favicon: FaviconURL?) {
|
@Environment(\.openURL) var openURL
|
||||||
|
|
||||||
|
init(nip05: NIP05, pubkey: Pubkey, contacts: Contacts, show_domain: Bool, profiles: Profiles) {
|
||||||
self.nip05 = nip05
|
self.nip05 = nip05
|
||||||
self.pubkey = pubkey
|
self.pubkey = pubkey
|
||||||
self.damus_state = damus_state
|
self.contacts = contacts
|
||||||
self.show_domain = show_domain
|
self.show_domain = show_domain
|
||||||
self.nip05_domain_favicon = nip05_domain_favicon
|
self.profiles = profiles
|
||||||
}
|
}
|
||||||
|
|
||||||
var nip05_color: Bool {
|
var nip05_color: Bool {
|
||||||
return use_nip05_color(pubkey: pubkey, contacts: damus_state.contacts)
|
return use_nip05_color(pubkey: pubkey, contacts: contacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
var Seal: some View {
|
var Seal: some View {
|
||||||
@@ -44,23 +44,8 @@ struct NIP05Badge: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var domainBadge: some View {
|
|
||||||
Group {
|
|
||||||
if let nip05_domain_favicon {
|
|
||||||
KFImage(nip05_domain_favicon.source)
|
|
||||||
.imageContext(.favicon, disable_animation: true)
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.frame(width: 18, height: 18)
|
|
||||||
.clipped()
|
|
||||||
} else {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var username_matches_nip05: Bool {
|
var username_matches_nip05: Bool {
|
||||||
guard let name = damus_state.profiles.lookup(id: pubkey)?.map({ p in p?.name }).value
|
guard let name = profiles.lookup(id: pubkey)?.map({ p in p?.name }).value
|
||||||
else {
|
else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -80,18 +65,14 @@ struct NIP05Badge: View {
|
|||||||
HStack(spacing: 2) {
|
HStack(spacing: 2) {
|
||||||
Seal
|
Seal
|
||||||
|
|
||||||
Group {
|
|
||||||
if show_domain {
|
if show_domain {
|
||||||
Text(nip05_string)
|
Text(nip05_string)
|
||||||
.nip05_colorized(gradient: nip05_color)
|
.nip05_colorized(gradient: nip05_color)
|
||||||
}
|
|
||||||
|
|
||||||
if nip05_domain_favicon != nil {
|
|
||||||
domainBadge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
damus_state.nav.push(route: Route.NIP05DomainEvents(events: NIP05DomainEventsModel(state: damus_state, domain: nip05.host), nip05_domain_favicon: nip05_domain_favicon))
|
if let nip5url = nip05.siteUrl {
|
||||||
|
openURL(nip5url)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,9 +98,13 @@ struct NIP05Badge_Previews: PreviewProvider {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let test_state = test_damus_state
|
let test_state = test_damus_state
|
||||||
VStack {
|
VStack {
|
||||||
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, damus_state: test_state, show_domain: true, nip05_domain_favicon: nil)
|
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
|
||||||
|
|
||||||
NIP05Badge(nip05: NIP05(username: "_", host: "jb55.com"), pubkey: test_state.pubkey, damus_state: test_state, show_domain: true, nip05_domain_favicon: nil)
|
NIP05Badge(nip05: NIP05(username: "_", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
|
||||||
|
|
||||||
|
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: test_state.contacts, show_domain: true, profiles: test_state.profiles)
|
||||||
|
|
||||||
|
NIP05Badge(nip05: NIP05(username: "jb55", host: "jb55.com"), pubkey: test_state.pubkey, contacts: Contacts(our_pubkey: test_pubkey), show_domain: true, profiles: test_state.profiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user