mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
some refactoring of io formatter internals
extracted logic for hex-buffers (%h), string-padding (%s), hex-floats (%a), and collection printing into helper functions.
This commit is contained in:
committed by
Christoffer Lerno
parent
9fbf6bc213
commit
7ae4c5a1ab
@@ -139,6 +139,47 @@ fn usz? Formatter.out_unknown(&self, String category, any arg) @private
|
||||
{
|
||||
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
|
||||
}
|
||||
fn usz? Formatter.out_collection(&self, any arg, String open, String close) @private
|
||||
{
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == void.typeid) inner = char.typeid;
|
||||
usz size = inner.sizeof;
|
||||
|
||||
usz alen;
|
||||
void* data_ptr;
|
||||
if (arg.type.kindof == SLICE)
|
||||
{
|
||||
String* temp = arg.ptr;
|
||||
data_ptr = temp.ptr;
|
||||
alen = temp.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
data_ptr = arg.ptr;
|
||||
alen = arg.type.len;
|
||||
}
|
||||
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
|
||||
usz len = self.out_substr(open)!;
|
||||
for (usz i = 0; i < alen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(data_ptr, inner))!;
|
||||
data_ptr += size;
|
||||
}
|
||||
len += self.out_substr(close)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out_str(&self, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
@@ -158,17 +199,9 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
unreachable();
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
|
||||
case FLOAT:
|
||||
case FUNC:
|
||||
case POINTER:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
@@ -178,7 +211,27 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
|
||||
case FLOAT:
|
||||
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
|
||||
case FUNC:
|
||||
case POINTER:
|
||||
if (arg.type.kindof == POINTER && arg.type.inner != void.typeid)
|
||||
{
|
||||
void** pointer = arg.ptr;
|
||||
any deref = any_make(*pointer, arg.type.inner);
|
||||
usz? n = self.print_with_function((Printable)deref);
|
||||
if (try n) return n;
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
}
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
case BOOL:
|
||||
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
||||
default:
|
||||
@@ -200,16 +253,6 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
return self.out_unknown("union", arg);
|
||||
case BITSTRUCT:
|
||||
return self.out_unknown("bitstruct", arg);
|
||||
case FUNC:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case CONST_ENUM:
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
@@ -225,102 +268,12 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
|
||||
}
|
||||
return self.out_str(arg.as_inner());
|
||||
case POINTER:
|
||||
typeid inner = arg.type.inner;
|
||||
void** pointer = arg.ptr;
|
||||
if (arg.type.inner != void.typeid)
|
||||
{
|
||||
any deref = any_make(*pointer, inner);
|
||||
n = self.print_with_function((Printable)deref);
|
||||
if (try n) return n;
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
}
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case ARRAY:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz alen = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
usz len = self.out('[')!;
|
||||
for (usz i = 0; i < alen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(ptr, inner))!;
|
||||
ptr += size;
|
||||
}
|
||||
len += self.out(']')!;
|
||||
return len;
|
||||
return self.out_collection(arg, "[", "]");
|
||||
case VECTOR:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz vlen = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
usz len = self.out_substr("[<")!;
|
||||
for (usz i = 0; i < vlen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(ptr, inner))!;
|
||||
ptr += size;
|
||||
}
|
||||
len += self.out_substr(">]")!;
|
||||
return len;
|
||||
return self.out_collection(arg, "[<", ">]");
|
||||
case SLICE:
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == void.typeid) inner = char.typeid;
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
usz size = inner.sizeof;
|
||||
// Pretend this is a String
|
||||
String* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usz slen = temp.len;
|
||||
usz len = self.out('[')!;
|
||||
for (usz i = 0; i < slen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(ptr, inner))!;
|
||||
ptr += size;
|
||||
}
|
||||
len += self.out(']')!;
|
||||
return len;
|
||||
return self.out_collection(arg, "[", "]");
|
||||
case ANY:
|
||||
case INTERFACE:
|
||||
unreachable("Already handled");
|
||||
@@ -361,6 +314,63 @@ macro usz? @wrap_bad(Formatter* f, #action)
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out_hex_buffer(&self, any arg) @private @inline
|
||||
{
|
||||
char[] out @noinit;
|
||||
switch (arg.type)
|
||||
{
|
||||
case char[]:
|
||||
case ichar[]:
|
||||
out = *(char[]*)arg;
|
||||
default:
|
||||
if (arg.type.kindof == ARRAY && (arg.type.inner == char.typeid || arg.type.inner == ichar.typeid))
|
||||
{
|
||||
out = ((char*)arg.ptr)[:arg.type.sizeof];
|
||||
break;
|
||||
}
|
||||
if (arg.type.kindof == POINTER)
|
||||
{
|
||||
// Maybe there is a more idiomatic way here
|
||||
out = ((*(char**)arg.ptr))[:arg.type.inner.sizeof];
|
||||
break;
|
||||
}
|
||||
return self.out_substr("<INVALID>");
|
||||
}
|
||||
usz len = out.len * 2;
|
||||
usz total;
|
||||
if (self.flags.left)
|
||||
{
|
||||
total += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total += self.pad(' ', self.width, (isz)total)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.width) total += self.pad(' ', self.width, (isz)len)!;
|
||||
total += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out_str_pad(&self, any arg) @private @inline
|
||||
{
|
||||
usz total;
|
||||
if (self.width && !self.flags.left)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(arg)!;
|
||||
self.out_fn = out_fn;
|
||||
total += self.pad(' ', self.width, (isz)len)!;
|
||||
}
|
||||
usz len = self.out_str(arg)!;
|
||||
total += len;
|
||||
if (self.flags.left)
|
||||
{
|
||||
total += self.pad(' ', self.width, (isz)len)!;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
{
|
||||
self.first_fault = {};
|
||||
@@ -415,7 +425,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
self.flags.left = true;
|
||||
w = -w;
|
||||
}
|
||||
self.width = w;
|
||||
self.width = (uint)w;
|
||||
// evaluate precision field
|
||||
self.prec = 0;
|
||||
if (c == '.')
|
||||
@@ -424,7 +434,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
|
||||
int? prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
|
||||
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
|
||||
self.prec = prec < 0 ? 0 : prec;
|
||||
self.prec = (uint)(prec < 0 ? 0 : prec);
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
@@ -488,56 +498,10 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'h':
|
||||
char[] out @noinit;
|
||||
switch (current.type)
|
||||
{
|
||||
case char[]:
|
||||
case ichar[]:
|
||||
out = *(char[]*)current;
|
||||
default:
|
||||
if (current.type.kindof == ARRAY && (current.type.inner == char.typeid || current.type.inner == ichar.typeid))
|
||||
{
|
||||
out = ((char*)current.ptr)[:current.type.sizeof];
|
||||
break;
|
||||
}
|
||||
if (current.type.kindof == POINTER)
|
||||
{
|
||||
out = ((*(char**)current.ptr))[:current.type.inner.sizeof];
|
||||
break;
|
||||
}
|
||||
total_len += self.out_substr("<INVALID>")!;
|
||||
continue;
|
||||
}
|
||||
if (self.flags.left)
|
||||
{
|
||||
usz len = print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total_len += len;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
if (self.width)
|
||||
{
|
||||
total_len += self.pad(' ', self.width, out.len * 2)!;
|
||||
}
|
||||
total_len += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total_len += self.out_hex_buffer(current)!;
|
||||
continue;
|
||||
case 's':
|
||||
if (self.flags.left)
|
||||
{
|
||||
usz len = self.out_str(current)!;
|
||||
total_len += len;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
if (self.width)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
total_len += self.out_str(current)!;
|
||||
total_len += self.out_str_pad(current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
self.flags.zeropad = true;
|
||||
@@ -559,10 +523,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
bool is_neg;
|
||||
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
if (self.first_fault) return self.first_fault~;
|
||||
return total_len;
|
||||
}
|
||||
|
||||
@@ -208,6 +208,70 @@ fn usz? Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y);
|
||||
fn usz? Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y);
|
||||
fn usz? Formatter.atoa(&self, double y) => self.floatformat(HEX, y);
|
||||
|
||||
fn usz? Formatter.floatformat_hex(&self, double y, bool is_neg, isz pl, isz p) @private @inline
|
||||
{
|
||||
double round = 8.0;
|
||||
// 0x / 0X
|
||||
pl += 2;
|
||||
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
|
||||
{
|
||||
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
|
||||
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
|
||||
while (re--) round *= 16;
|
||||
if (is_neg)
|
||||
{
|
||||
y = -y;
|
||||
y -= round;
|
||||
y += round;
|
||||
y = -y;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += round;
|
||||
y -= round;
|
||||
}
|
||||
}
|
||||
int e2;
|
||||
y = math::frexp(y, &e2) * 2;
|
||||
if (y) e2--;
|
||||
char[12] ebuf0;
|
||||
char* ebuf = &ebuf0[0] + 12;
|
||||
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
|
||||
char* buf = &buf_array[0];
|
||||
|
||||
// Reverse print
|
||||
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
|
||||
if (estr == ebuf) *--estr = '0';
|
||||
*--estr = (e2 < 0 ? '-' : '+');
|
||||
*--estr = self.flags.uppercase ? 'P' : 'p';
|
||||
char* s = buf;
|
||||
char* xdigits = self.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
|
||||
do
|
||||
{
|
||||
int x = (int)y;
|
||||
*s++ = xdigits[x];
|
||||
y = 16 * (y - x);
|
||||
if (s - buf == 1 && (y || p > 0 || self.flags.hash)) *s++ = '.';
|
||||
if (p >= 0 && (s - buf) >= (2 + p)) break;
|
||||
} while (y);
|
||||
isz outlen = s - buf;
|
||||
isz explen = ebuf - estr;
|
||||
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
usz len;
|
||||
usz l = (usz)(p && outlen - 2 < p
|
||||
? p + 2 + explen
|
||||
: outlen + explen);
|
||||
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
|
||||
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
|
||||
len += self.out_chars(buf[:outlen])!;
|
||||
len += self.pad('0', (isz)l - outlen - explen, 0)!;
|
||||
len += self.out_chars(estr[:explen])!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, pl + (isz)l)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @private
|
||||
{
|
||||
// This code is heavily based on musl's printf code
|
||||
@@ -234,70 +298,15 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
isz p = self.flags.precision ? self.prec : -1;
|
||||
if (formatting == HEX) return self.floatformat_hex(y, is_neg, pl, p);
|
||||
|
||||
// Rescale
|
||||
int e2;
|
||||
|
||||
y = math::frexp(y, &e2) * 2;
|
||||
if (y) e2--;
|
||||
char[12] ebuf0;
|
||||
char* ebuf = 12 + (char*)&ebuf0;
|
||||
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
|
||||
char* buf = &buf_array;
|
||||
isz p = self.flags.precision ? self.prec : -1;
|
||||
if (formatting == HEX)
|
||||
{
|
||||
double round = 8.0;
|
||||
// 0x / 0X
|
||||
pl += 2;
|
||||
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
|
||||
{
|
||||
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
|
||||
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
|
||||
while (re--) round *= 16;
|
||||
if (is_neg)
|
||||
{
|
||||
y = -y;
|
||||
y -= round;
|
||||
y += round;
|
||||
y = -y;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += round;
|
||||
y -= round;
|
||||
}
|
||||
}
|
||||
// Reverse print
|
||||
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
|
||||
if (estr == ebuf) *--estr = '0';
|
||||
*--estr = (e2 < 0 ? '-' : '+');
|
||||
*--estr = self.flags.uppercase ? 'P' : 'p';
|
||||
char* s = buf;
|
||||
char* xdigits = self.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
|
||||
do
|
||||
{
|
||||
int x = (int)y;
|
||||
*s++ = xdigits[x];
|
||||
y = 16 * (y - x);
|
||||
if (s - buf == 1 && (y || p > 0 || self.flags.hash)) *s++ = '.';
|
||||
} while (y);
|
||||
isz outlen = s - buf;
|
||||
isz explen = ebuf - estr;
|
||||
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
usz len;
|
||||
usz l = p && outlen - 2 < p
|
||||
? p + 2 + explen
|
||||
: outlen + explen;
|
||||
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
|
||||
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
|
||||
len += self.out_chars(buf[:outlen])!;
|
||||
len += self.pad('0', l - outlen - explen, 0)!;
|
||||
len += self.out_chars(estr[:explen])!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, pl + l)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
if (p < 0) p = 6;
|
||||
if (y)
|
||||
{
|
||||
@@ -344,7 +353,6 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
|
||||
for (uint* d = a; d < z; d++)
|
||||
{
|
||||
// CHECK THIS
|
||||
uint rm = *d & ((1 << sh) - 1);
|
||||
*d = (*d >> sh) + carry;
|
||||
carry = (1000000000 >> sh) * rm;
|
||||
@@ -456,6 +464,8 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
}
|
||||
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
int l = (int)(1 + p + (isz)(p || self.flags.hash));
|
||||
char[12] ebuf0;
|
||||
char* ebuf = &ebuf0[0] + 12;
|
||||
char* estr @noinit;
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
@@ -476,6 +486,10 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
|
||||
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
|
||||
|
||||
char[9] buf_array;
|
||||
char* buf = &buf_array[0];
|
||||
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
if (a > r) a = r;
|
||||
|
||||
Reference in New Issue
Block a user