Change armv4t integer to string conversion to use an unrolled loop
This implementation also writes the digits from left to right instead of right to left. Using this method we can write the string to the beginning of the buffer and still avoid reversing the string. It also has the benefit of being slightly slower than the previous implementation. The function's signature changed as well because there is no longer a reason to pass the buffer size or a pointer to output the start of the string.
This commit is contained in:
parent
b00a52ea9b
commit
1159162e50
@ -17,10 +17,11 @@ void mtl_hybridmove(void* dst, const void* src, size_t num8);
|
|||||||
|
|
||||||
uint32_t mtl_udiv10(uint32_t x);
|
uint32_t mtl_udiv10(uint32_t x);
|
||||||
uint32_t mtl_umod10(uint32_t x);
|
uint32_t mtl_umod10(uint32_t x);
|
||||||
uint32_t mtl_utostr(uint32_t x, char* buf, size_t buf_size, char** buf_out);
|
|
||||||
uint32_t mtl_utostrx(uint32_t x, char* buf, size_t buf_size, char** buf_out);
|
uint32_t mtl_utostr(uint32_t x, char* buf);
|
||||||
uint32_t mtl_itostr(uint32_t x, char* buf, size_t buf_size, char** buf_out);
|
uint32_t mtl_utostrx(uint32_t x, char* buf);
|
||||||
uint32_t mtl_itostrx(uint32_t x, char* buf, size_t buf_size, char** buf_out);
|
uint32_t mtl_itostr(int32_t x, char* buf);
|
||||||
|
uint32_t mtl_itostrx(int32_t x, char* buf);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
534
src/armv4t/to.s
534
src/armv4t/to.s
@ -2,244 +2,350 @@
|
|||||||
|
|
||||||
.include "mtl/armv4t/asm/math.s"
|
.include "mtl/armv4t/asm/math.s"
|
||||||
|
|
||||||
.section .rodata
|
|
||||||
|
|
||||||
.align 1
|
|
||||||
hex_digits_lo:
|
|
||||||
.string "0123456789abcdef"
|
|
||||||
hex_digits_up:
|
|
||||||
.string "0123456789ABCDEF"
|
|
||||||
|
|
||||||
.section .iwram, "ax", %progbits
|
.section .iwram, "ax", %progbits
|
||||||
.arm
|
.arm
|
||||||
.align 2
|
.align 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Writes the ASCII representation of the given unsigned value X to the buffer,
|
* Writes the ASCII representation of the given signed value X to the buffer,
|
||||||
* writes the beginning address of the string to an output ptr, and returns
|
* returns the number of characters written. The value is written in decimal
|
||||||
* the number of characters written. The value is written in hexidecimal
|
* format and is null terminated. The string will always begin at the start of
|
||||||
* format.
|
* the buffer.
|
||||||
*
|
*
|
||||||
* r0 = x
|
* r0 = x
|
||||||
* r1 = buffer start
|
* r1 = buffer
|
||||||
* r2 = buffer size
|
|
||||||
* r3 = string start out ptr
|
|
||||||
* ret= num of characters written
|
* ret= num of characters written
|
||||||
*/
|
*/
|
||||||
.global mtl_utostrx
|
.global mtl_itostr
|
||||||
.type mtl_utostrx STT_FUNC
|
.type mtl_itostr STT_FUNC
|
||||||
mtl_utostrx:
|
mtl_itostr:
|
||||||
// Store variables
|
// Branch immediately if the number is positive
|
||||||
push {r4-r7}
|
lsrs r12, r0, $31
|
||||||
// Move buffer to end
|
beq mtl_utostr
|
||||||
add r1, r2
|
// Write the negative sign to the string
|
||||||
sub r1, $1
|
mvn r0, r0
|
||||||
// Add null terminator and decrement
|
add r0, $1
|
||||||
mov r6, $0
|
// Get the absolute value of the number
|
||||||
str r6, [r1], $-1
|
mov r12, '-'
|
||||||
// Load digit address
|
strb r12, [r1], $1
|
||||||
ldr r4, =hex_digits_lo
|
// Write abs(x) to the string
|
||||||
// Zero num of characters
|
push {fp, lr}
|
||||||
mov r7, $0
|
bl mtl_utostr
|
||||||
.Ldecode_digitx:
|
pop {fp, lr}
|
||||||
// Increment number of characters
|
// One more character was written to the string ('-')
|
||||||
add r7, $1
|
add r0, $1
|
||||||
// Get least significant octet
|
bx lr
|
||||||
and r6, r0, 0xF
|
|
||||||
// Write the ascii digit to the string
|
/*
|
||||||
ldrb r5, [r4, r6]
|
* Writes the ASCII representation of the given signed value X to the buffer,
|
||||||
strb r5, [r1], $-1
|
* returns the number of characters written. The value is written in hexadecimal
|
||||||
// Shift to remove the least significant octet
|
* format and is null terminated. The string will always begin at the start of
|
||||||
lsrs r0, $4
|
* the buffer.
|
||||||
// Repeat if there are more digits left
|
*
|
||||||
bne .Ldecode_digitx
|
* r0 = x
|
||||||
// Move to the start of the string and account for "0x",
|
* r1 = buffer
|
||||||
// currently one character before
|
* ret= num of characters written
|
||||||
sub r1, $1
|
*/
|
||||||
//add r1, $1
|
.global mtl_itostrx
|
||||||
// Write "0x" to the beginning of the string
|
.type mtl_itostrx STT_FUNC
|
||||||
mov r6, '0'
|
mtl_itostrx:
|
||||||
strb r6, [r1]
|
// Branch immediately if the number is positive
|
||||||
mov r6, 'x'
|
lsrs r12, r0, $31
|
||||||
strb r6, [r1, $1]
|
beq mtl_utostrx
|
||||||
// Write the start of the string to the pointer
|
// Write the negative sign to the string
|
||||||
str r1, [r3]
|
mov r12, '-'
|
||||||
// Return the number of characters written, account for "0x"
|
strb r12, [r1], $1
|
||||||
add r0, r7, $2
|
// Get the absolute value of the number
|
||||||
// Restore variables
|
mvn r0, r0
|
||||||
pop {r4-r7}
|
add r0, $1
|
||||||
|
// Write abs(x) to the string
|
||||||
|
push {fp, lr}
|
||||||
|
bl mtl_utostrx
|
||||||
|
pop {fp, lr}
|
||||||
|
// One more character was written to the string ('-')
|
||||||
|
add r0, $1
|
||||||
bx lr
|
bx lr
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Writes the ASCII representation of the given unsigned value X to the buffer,
|
* Writes the ASCII representation of the given unsigned value X to the buffer,
|
||||||
* writes the beginning address of the string to an output ptr, and returns
|
* returns the number of characters written. The value is written in decimal
|
||||||
* the number of characters written. The value is written in decimal format.
|
* format and is null terminated. The string will always begin at the start of
|
||||||
|
* the buffer.
|
||||||
|
*
|
||||||
|
* Instead of using a loop like the previous implementation, this implementation
|
||||||
|
* uses a manually unrolled loop. This implementation also writes the digits
|
||||||
|
* from left to right instead of right to left. Using this method we can write
|
||||||
|
* the string to the beginning of the buffer and still avoid reversing the string.
|
||||||
*
|
*
|
||||||
* r0 = x
|
* r0 = x
|
||||||
* r1 = buffer start
|
* r1 = buffer
|
||||||
* r2 = buffer size
|
|
||||||
* r3 = string start out ptr
|
|
||||||
* ret= num of characters written
|
* ret= num of characters written
|
||||||
*/
|
*/
|
||||||
.global mtl_utostr
|
.global mtl_utostr
|
||||||
.type mtl_utostr STT_FUNC
|
.type mtl_utostr STT_FUNC
|
||||||
mtl_utostr:
|
mtl_utostr:
|
||||||
// Store variables
|
// Store variables
|
||||||
push {r4-r7}
|
push {r4-r6}
|
||||||
// Move buffer to end
|
// Temporary used to calculate 10^P
|
||||||
add r1, r2
|
mov r5, $10
|
||||||
sub r1, $1
|
// Minimum of 1 digit
|
||||||
// Add null terminator and decrement
|
mov r3, $1
|
||||||
mov r6, $0
|
// If less than 10, one digit, otherwise more
|
||||||
str r6, [r1], $-1
|
mov r4, $10
|
||||||
// Zero num of characters
|
cmp r0, r4
|
||||||
mov r7, $0
|
blo .Ldigits_1
|
||||||
.Ldecode_digit:
|
// Now 2 digits
|
||||||
// Increment number of characters
|
add r3, $1
|
||||||
add r7, $1
|
// If less than 100, two digits, otherwise more
|
||||||
// Use % 10 to get the least significant digit
|
mul r4, r5
|
||||||
umod10 r6, r0, r12
|
cmp r0, r4
|
||||||
// Write the ascii digit to the string
|
blo .Ldigits_2
|
||||||
add r5, r6, '0'
|
// Repeat...
|
||||||
strb r5, [r1], $-1
|
add r3, $1
|
||||||
// Divide by 10 to remove the least significant digit
|
mul r4, r5
|
||||||
udiv10 r6, r0, r12
|
cmp r0, r4
|
||||||
mov r0, r6
|
blo .Ldigits_3
|
||||||
// Repeat if there are more digits left
|
add r3, $1
|
||||||
bne .Ldecode_digit
|
mul r4, r5
|
||||||
// Move to the start of the string, currently one char before
|
cmp r0, r4
|
||||||
add r1, $1
|
blo .Ldigits_4
|
||||||
// Write the start of the string to the pointer
|
add r3, $1
|
||||||
str r1, [r3]
|
mul r4, r5
|
||||||
// Return the number of characters written
|
cmp r0, r4
|
||||||
mov r0, r7
|
blo .Ldigits_5
|
||||||
// Restore variables
|
add r3, $1
|
||||||
pop {r4-r7}
|
mul r4, r5
|
||||||
bx lr
|
cmp r0, r4
|
||||||
|
blo .Ldigits_6
|
||||||
|
add r3, $1
|
||||||
|
mul r4, r5
|
||||||
|
cmp r0, r4
|
||||||
|
blo .Ldigits_7
|
||||||
|
add r3, $1
|
||||||
|
mul r4, r5
|
||||||
|
cmp r0, r4
|
||||||
|
blo .Ldigits_8
|
||||||
|
add r3, $1
|
||||||
|
mul r4, r5
|
||||||
|
cmp r0, r4
|
||||||
|
blo .Ldigits_9
|
||||||
|
// 10 digits
|
||||||
|
add r3, $1
|
||||||
|
.Ldigits_10:
|
||||||
|
// Divide by 10^9 to get the most significant digit
|
||||||
|
udiv1000000000 r5, r0, r12
|
||||||
|
// Write the digit to the string
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
// Subtract the most significant digit
|
||||||
|
ldr r6, =1000000000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_9:
|
||||||
|
// Repeat...
|
||||||
|
udiv100000000 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
ldr r6, =100000000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_8:
|
||||||
|
udiv10000000 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
ldr r6, =10000000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_7:
|
||||||
|
udiv1000000 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
ldr r6, =1000000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_6:
|
||||||
|
udiv100000 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
ldr r6, =100000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_5:
|
||||||
|
udiv10000 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
ldr r6, =10000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_4:
|
||||||
|
udiv1000 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
mov r6, $1000
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_3:
|
||||||
|
udiv100 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
mov r6, $100
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_2:
|
||||||
|
udiv10 r5, r0, r12
|
||||||
|
add r6, r5, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
mov r6, $10
|
||||||
|
mul r6, r5
|
||||||
|
sub r0, r6
|
||||||
|
.Ldigits_1:
|
||||||
|
add r6, r0, '0'
|
||||||
|
strb r6, [r1], $1
|
||||||
|
|
||||||
/*
|
// Write the null terminator
|
||||||
* Writes the ASCII representation of the given signed value X to the buffer,
|
|
||||||
* writes the beginning address of the string to an output ptr, and returns
|
|
||||||
* the number of characters written. The value is written in hexidecimal
|
|
||||||
* format.
|
|
||||||
*
|
|
||||||
* r0 = x
|
|
||||||
* r1 = buffer start
|
|
||||||
* r2 = buffer size
|
|
||||||
* r3 = string start out ptr
|
|
||||||
* ret= num of characters written
|
|
||||||
*/
|
|
||||||
.global mtl_itostrx
|
|
||||||
.type mtl_itostrx STT_FUNC
|
|
||||||
mtl_itostrx:
|
|
||||||
// Store variables
|
|
||||||
push {r4-r8}
|
|
||||||
// r8 = 1 if negative, 0 if positive. Negate if negative
|
|
||||||
lsrs r8, r0, $31
|
|
||||||
mvnne r0, r0
|
|
||||||
addne r0, $1
|
|
||||||
// Move buffer to end
|
|
||||||
add r1, r2
|
|
||||||
sub r1, $1
|
|
||||||
// Add null terminator and decrement
|
|
||||||
mov r6, $0
|
mov r6, $0
|
||||||
str r6, [r1], $-1
|
|
||||||
// Load digit address
|
|
||||||
ldr r4, =hex_digits_lo
|
|
||||||
// Zero num of characters
|
|
||||||
mov r7, $0
|
|
||||||
.Lsdecode_digitx:
|
|
||||||
// Increment number of characters
|
|
||||||
add r7, $1
|
|
||||||
// Get least significant octet
|
|
||||||
and r6, r0, 0xF
|
|
||||||
// Write the ascii digit to the string
|
|
||||||
ldrb r5, [r4, r6]
|
|
||||||
strb r5, [r1], $-1
|
|
||||||
// Shift to remove the least significant octet
|
|
||||||
lsrs r0, $4
|
|
||||||
// Repeat if there are more digits left
|
|
||||||
bne .Lsdecode_digitx
|
|
||||||
// Move to the start of the string and account for "0x",
|
|
||||||
// currently one character before
|
|
||||||
sub r1, $1
|
|
||||||
// Write "0x" to the beginning of the string
|
|
||||||
mov r6, '0'
|
|
||||||
strb r6, [r1]
|
strb r6, [r1]
|
||||||
mov r6, 'x'
|
// Return number of digits written
|
||||||
strb r6, [r1, $1]
|
mov r0, r3
|
||||||
// Check if number is negative
|
|
||||||
cmp r8, $0
|
|
||||||
// Add the negative sign to the string and increase number of
|
|
||||||
// characters written if needed
|
|
||||||
subne r1, $1
|
|
||||||
movne r6, '-'
|
|
||||||
strbne r6, [r1]
|
|
||||||
addne r7, $1
|
|
||||||
// Write the start of the string to the pointer
|
|
||||||
str r1, [r3]
|
|
||||||
// Return the number of characters written, account for "0x"
|
|
||||||
add r0, r7, $2
|
|
||||||
// Restore variables
|
// Restore variables
|
||||||
pop {r4-r8}
|
pop {r4-r6}
|
||||||
bx lr
|
bx lr
|
||||||
/*
|
|
||||||
* Writes the ASCII representation of the given signed value X to the buffer,
|
/*
|
||||||
* writes the beginning address of the string to an output ptr, and returns
|
* Writes the ASCII representation of the given unsigned value X to the buffer,
|
||||||
* the number of characters written. The value is written in decimal format.
|
* returns the number of characters written. The value is written in hexadecimal
|
||||||
*
|
* format and is null terminated. The string will always begin at the start of
|
||||||
* r0 = x
|
* the buffer.
|
||||||
* r1 = buffer start
|
*
|
||||||
* r2 = buffer size
|
* Instead of using a loop like the previous implementation, this implementation
|
||||||
* r3 = string start out ptr
|
* uses a manually unrolled loop. This implementation also writes the digits
|
||||||
* ret= num of characters written
|
* from left to right instead of right to left. Using this method we can write
|
||||||
*/
|
* the string to the beginning of the buffer and still avoid reversing the string.
|
||||||
.global mtl_itostr
|
*
|
||||||
.type mtl_itostr STT_FUNC
|
* r0 = x
|
||||||
mtl_itostr:
|
* r1 = buffer
|
||||||
// Store variables
|
* ret= num of characters written
|
||||||
push {r4-r8}
|
*/
|
||||||
// r8 = 1 if negative, 0 if positive. Negate if negative
|
.global mtl_utostrx
|
||||||
lsrs r8, r0, $31
|
.type mtl_utostrx STT_FUNC
|
||||||
mvnne r0, r0
|
mtl_utostrx:
|
||||||
addne r0, $1
|
// Store variables
|
||||||
// Move buffer to end
|
push {r4-r6}
|
||||||
add r1, r2
|
// Write "0x" to the string
|
||||||
sub r1, $1
|
mov r6, '0'
|
||||||
// Add null terminator and decrement
|
strb r6, [r1], $1
|
||||||
mov r6, $0
|
mov r6, 'x'
|
||||||
str r6, [r1], $-1
|
strb r6, [r1], $1
|
||||||
// Zero num of characters
|
|
||||||
mov r7, $0
|
// Minimum of 3 characters (including "0x")
|
||||||
.Lsdecode_digit:
|
mov r3, $3
|
||||||
// Increment number of characters
|
// If less than 0x10, one digit, otherwise more
|
||||||
add r7, $1
|
mov r4, $0x10
|
||||||
// Use % 10 to get the least significant digit
|
cmp r0, r4
|
||||||
umod10 r6, r0, r12
|
blo .Lxdigits_1
|
||||||
// Write the ascii digit to the string
|
// Now four characters
|
||||||
add r5, r6, '0'
|
add r3, $1
|
||||||
strb r5, [r1], $-1
|
// If less than 0x100, two digits, otherwise more
|
||||||
// Divide by 10 to remove the least significant digit
|
lsl r4, $4
|
||||||
udiv10 r6, r0, r12
|
cmp r0, r4
|
||||||
mov r0, r6
|
blo .Lxdigits_2
|
||||||
// Repeat if there are more digits left
|
// Repeat...
|
||||||
bne .Lsdecode_digit
|
add r3, $1
|
||||||
// Check if number is negative
|
lsl r4, $4
|
||||||
cmp r8, $0
|
cmp r0, r4
|
||||||
// Add the negative sign to the string and increase number of
|
blo .Lxdigits_3
|
||||||
// characters written if needed
|
add r3, $1
|
||||||
movne r6, '-'
|
lsl r4, $4
|
||||||
strbne r6, [r1]
|
cmp r0, r4
|
||||||
addne r7, $1
|
blo .Lxdigits_4
|
||||||
// If the number was positive, currently positioned one char before str
|
add r3, $1
|
||||||
// If the number was negative, currently at the correct position
|
lsl r4, $4
|
||||||
// Move one character forward if needed
|
cmp r0, r4
|
||||||
addeq r1, $1
|
blo .Lxdigits_5
|
||||||
// Write the start of the string to the pointer
|
add r3, $1
|
||||||
str r1, [r3]
|
lsl r4, $4
|
||||||
// Return the number of characters written
|
cmp r0, r4
|
||||||
mov r0, r7
|
blo .Lxdigits_6
|
||||||
// Restore variables
|
add r3, $1
|
||||||
pop {r4-r8}
|
lsl r4, $4
|
||||||
|
cmp r0, r4
|
||||||
|
blo .Lxdigits_7
|
||||||
|
// Eight digits
|
||||||
|
add r3, $1
|
||||||
|
.Lxdigits_8:
|
||||||
|
// Right shift to get the most significant digit
|
||||||
|
lsr r5, r0, $28
|
||||||
|
// Get digit character
|
||||||
|
add r6, r5, '0'
|
||||||
|
// If the digit > 9, change to a lowercase letter
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
// Write the digit
|
||||||
|
strb r6, [r1], $1
|
||||||
|
// Subtract the digit
|
||||||
|
lsl r5, $28
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_7:
|
||||||
|
// Repeat...
|
||||||
|
lsr r5, r0, $24
|
||||||
|
add r6, r5, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
lsl r5, $24
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_6:
|
||||||
|
lsr r5, r0, $20
|
||||||
|
add r6, r5, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
lsl r5, $20
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_5:
|
||||||
|
lsr r5, r0, $16
|
||||||
|
add r6, r5, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
lsl r5, $16
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_4:
|
||||||
|
lsr r5, r0, $12
|
||||||
|
add r6, r5, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
lsl r5, $12
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_3:
|
||||||
|
lsr r5, r0, $8
|
||||||
|
add r6, r5, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
lsl r5, $8
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_2:
|
||||||
|
lsr r5, r0, $4
|
||||||
|
add r6, r5, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
lsl r5, $4
|
||||||
|
sub r0, r5
|
||||||
|
.Lxdigits_1:
|
||||||
|
add r6, r0, '0'
|
||||||
|
cmp r6, '9'
|
||||||
|
addhi r6, $0x27
|
||||||
|
strb r6, [r1], $1
|
||||||
|
|
||||||
|
// Write the null terminator
|
||||||
|
mov r6, $0
|
||||||
|
strb r6, [r1]
|
||||||
|
// Return the number of digits written
|
||||||
|
mov r0, r3
|
||||||
|
// Restore variables
|
||||||
|
pop {r4-r6}
|
||||||
bx lr
|
bx lr
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user