From 445f276018511db52de37babae2fefe7ad60f8af Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Mon, 18 Dec 2023 23:20:31 -0600 Subject: [PATCH] Progress: Further work in TacocaT. Build the entire packet now in one buffer. Fix some mistakes in the packet too-small/too-large status codes. Change the send packets are built. Setup the random seed and add a proof of concept salt header. --- sources/c/tacocat/main/common/define.h | 2 +- sources/c/tacocat/main/common/enumeration.h | 34 +++--- sources/c/tacocat/main/common/print.c | 4 +- sources/c/tacocat/main/common/print.h | 4 +- sources/c/tacocat/main/common/string.c | 1 + sources/c/tacocat/main/common/string.h | 3 + sources/c/tacocat/main/common/type.h | 1 + sources/c/tacocat/main/receive.c | 8 +- sources/c/tacocat/main/send.c | 178 ++++++++++++++++------------ sources/c/tacocat/main/tacocat.c | 3 + 10 files changed, 139 insertions(+), 99 deletions(-) diff --git a/sources/c/tacocat/main/common/define.h b/sources/c/tacocat/main/common/define.h index 4f07a0a..15c6da8 100644 --- a/sources/c/tacocat/main/common/define.h +++ b/sources/c/tacocat/main/common/define.h @@ -73,7 +73,7 @@ extern "C" { #define kt_tacocat_max_buffer_d 0x10000000 // 0x10^0x5 * 0x100 (Which is 256 Megabytes (0x10^0x5 where the base unit is 16 rather than 10 or 2 (maybe call this xytes? Megaxytes?)). #define kt_tacocat_max_maintain_d 0x100000 // 0x10^5 (Which is 1 Megabyte in base 16 (1 Megaxyte (MX)). - #define kt_tacocat_packet_headers_d 0x6 + #define kt_tacocat_packet_headers_d 0x7 #define kt_tacocat_packet_minimum_d 0x11 #define kt_tacocat_packet_peek_d 0x40 #define kt_tacocat_packet_prebuffer_d 0x200 diff --git a/sources/c/tacocat/main/common/enumeration.h b/sources/c/tacocat/main/common/enumeration.h index 93ba773..747e715 100644 --- a/sources/c/tacocat/main/common/enumeration.h +++ b/sources/c/tacocat/main/common/enumeration.h @@ -146,25 +146,27 @@ extern "C" { * Individual socket-specific flags for receiving. * * kt_tacocat_socket_flag_send_*_e: - * - none: No flags set. - * - size: Determine the file size. - * - file: Buffer the file. - * - build: Build the header information. - * - combine: Combine the built header information into a single buffer. - * - header: Send the header information. - * - payload: Send the payload information. - * - done: The entire Packet is sent. + * - none: No flags set. + * - size: Determine the file size. + * - header: Build and buffer the header. + * - build: Build the header information. + * - file: Buffer the file. + * - check: Additional checks before sending, such as re-checking header size. + * - encode: Encode entire packet. + * - packet: Send the entire packet. + * - done: The entire Packet is sent. */ #ifndef _di_kt_tacocat_socket_flag_send_e_ enum { - kt_tacocat_socket_flag_send_none_e = 0x0, - kt_tacocat_socket_flag_send_size_e = 0x1, - kt_tacocat_socket_flag_send_file_e = 0x2, - kt_tacocat_socket_flag_send_build_e = 0x4, - kt_tacocat_socket_flag_send_combine_e = 0x8, - kt_tacocat_socket_flag_send_header_e = 0x10, - kt_tacocat_socket_flag_send_payload_e = 0x20, - kt_tacocat_socket_flag_send_done_e = 0x40, + kt_tacocat_socket_flag_send_none_e = 0x0, + kt_tacocat_socket_flag_send_size_e = 0x1, + kt_tacocat_socket_flag_send_header_e = 0x2, + kt_tacocat_socket_flag_send_build_e = 0x4, + kt_tacocat_socket_flag_send_file_e = 0x8, + kt_tacocat_socket_flag_send_check_e = 0x10, + kt_tacocat_socket_flag_send_encode_e = 0x20, + kt_tacocat_socket_flag_send_packet_e = 0x40, + kt_tacocat_socket_flag_send_done_e = 0x80, }; // enum #endif // _di_kt_tacocat_socket_flag_send_e_ diff --git a/sources/c/tacocat/main/common/print.c b/sources/c/tacocat/main/common/print.c index f19dea3..7596c25 100644 --- a/sources/c/tacocat/main/common/print.c +++ b/sources/c/tacocat/main/common/print.c @@ -11,9 +11,11 @@ extern "C" { "f_file_close_id", "f_file_open", "f_file_read_block", + "f_file_seek", "f_file_size_by_id", "f_file_write", - "f_fss_simple_packet_extract_range", + "f_fss_simple_packet_decode_range", + "f_fss_simple_packet_encode", "f_memory_array_increase_by", "f_memory_array_resize", "f_network_from_ip_name", diff --git a/sources/c/tacocat/main/common/print.h b/sources/c/tacocat/main/common/print.h index 03d35bb..9d1e8d1 100644 --- a/sources/c/tacocat/main/common/print.h +++ b/sources/c/tacocat/main/common/print.h @@ -44,9 +44,11 @@ extern "C" { kt_tacocat_f_f_file_close_id_e, kt_tacocat_f_f_file_open_e, kt_tacocat_f_f_file_read_block_e, + kt_tacocat_f_f_file_seek_e, kt_tacocat_f_f_file_size_by_id_e, kt_tacocat_f_f_file_write_e, - kt_tacocat_f_f_fss_simple_packet_extract_range_e, + kt_tacocat_f_f_fss_simple_packet_decode_range_e, + kt_tacocat_f_f_fss_simple_packet_encode_e, kt_tacocat_f_f_memory_array_increase_by_e, kt_tacocat_f_f_memory_array_resize_e, kt_tacocat_f_f_network_from_ip_name_e, diff --git a/sources/c/tacocat/main/common/string.c b/sources/c/tacocat/main/common/string.c index acd2ec0..071a743 100644 --- a/sources/c/tacocat/main/common/string.c +++ b/sources/c/tacocat/main/common/string.c @@ -25,6 +25,7 @@ extern "C" { const f_string_static_t kt_tacocat_network_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_network_s, 0, KT_TACOCAT_network_s_length); const f_string_static_t kt_tacocat_network_or_socket_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_network_or_socket_s, 0, KT_TACOCAT_network_or_socket_s_length); const f_string_static_t kt_tacocat_receive_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_receive_s, 0, KT_TACOCAT_receive_s_length); + const f_string_static_t kt_tacocat_salt_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_salt_s, 0, KT_TACOCAT_salt_s_length); const f_string_static_t kt_tacocat_send_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_send_s, 0, KT_TACOCAT_send_s_length); const f_string_static_t kt_tacocat_send_build_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_send_build_s, 0, KT_TACOCAT_send_build_s_length); const f_string_static_t kt_tacocat_send_combine_s = macro_f_string_static_t_initialize_1(KT_TACOCAT_send_combine_s, 0, KT_TACOCAT_send_combine_s_length); diff --git a/sources/c/tacocat/main/common/string.h b/sources/c/tacocat/main/common/string.h index bcb0713..c53b3b2 100644 --- a/sources/c/tacocat/main/common/string.h +++ b/sources/c/tacocat/main/common/string.h @@ -79,6 +79,7 @@ extern "C" { #define KT_TACOCAT_network_s "network" #define KT_TACOCAT_network_or_socket_s "network / socket" #define KT_TACOCAT_receive_s "receive" + #define KT_TACOCAT_salt_s "salt" #define KT_TACOCAT_send_s "send" #define KT_TACOCAT_send_build_s "send build" #define KT_TACOCAT_send_combine_s "send combine" @@ -99,6 +100,7 @@ extern "C" { #define KT_TACOCAT_network_s_length 7 #define KT_TACOCAT_network_or_socket_s_length 16 #define KT_TACOCAT_receive_s_length 7 + #define KT_TACOCAT_salt_s_length 4 #define KT_TACOCAT_send_s_length 4 #define KT_TACOCAT_send_build_s_length 19 #define KT_TACOCAT_send_combine_s_length 12 @@ -119,6 +121,7 @@ extern "C" { extern const f_string_static_t kt_tacocat_network_s; extern const f_string_static_t kt_tacocat_network_or_socket_s; extern const f_string_static_t kt_tacocat_receive_s; + extern const f_string_static_t kt_tacocat_salt_s; extern const f_string_static_t kt_tacocat_send_s; extern const f_string_static_t kt_tacocat_send_build_s; extern const f_string_static_t kt_tacocat_send_combine_s; diff --git a/sources/c/tacocat/main/common/type.h b/sources/c/tacocat/main/common/type.h index 253fbc9..7513334 100644 --- a/sources/c/tacocat/main/common/type.h +++ b/sources/c/tacocat/main/common/type.h @@ -21,6 +21,7 @@ extern "C" { * * size_block: The size in bytes to used to represent a block when sending or receiving packets. * size_done: The size in bytes that are done being processed (generally used by send/write). + * size_total: The size in bytes tht represent the entire size to be processed (size_done should eventually equal this). * * flag: A set of flags. * retry: The current number of retries performed. diff --git a/sources/c/tacocat/main/receive.c b/sources/c/tacocat/main/receive.c index e091e48..e58f8c5 100644 --- a/sources/c/tacocat/main/receive.c +++ b/sources/c/tacocat/main/receive.c @@ -225,8 +225,8 @@ extern "C" { return; } - set->status = f_fss_simple_packet_extract_range(set->buffer, &set->packet); - macro_kt_receive_process_begin_handle_error_exit_1(main, f_fss_simple_packet_extract_range, set->network, set->status, set->name, set->flag); + set->status = f_fss_simple_packet_decode_range(set->buffer, &set->packet); + macro_kt_receive_process_begin_handle_error_exit_1(main, f_fss_simple_packet_decode_range, set->network, set->status, set->name, set->flag); if (set->status == F_packet_too_small || set->packet.size < kt_tacocat_packet_minimum_d || set->packet.size > main->setting.max_buffer) { set->buffer.used = 0; @@ -246,10 +246,10 @@ extern "C" { set->retry = 0; if (set->status == F_packet_too_small || set->packet.size < kt_tacocat_packet_minimum_d) { - set->status = F_status_set_error(F_packet_too_large); + set->status = F_status_set_error(F_packet_too_small); } else { - set->status = F_status_set_error(F_packet_too_small); + set->status = F_status_set_error(F_packet_too_large); } kt_tacocat_print_error_on_packet_invalid(&main->program.error, kt_tacocat_receive_s, set->network); diff --git a/sources/c/tacocat/main/send.c b/sources/c/tacocat/main/send.c index c468e8e..fac69bf 100644 --- a/sources/c/tacocat/main/send.c +++ b/sources/c/tacocat/main/send.c @@ -82,22 +82,20 @@ extern "C" { set->header.used = 0; set->headers.used = 0; set->size_done = 0; + set->size_total = 0; + set->file.size_read = set->size_block; // Initialize the default file payload. set->status = f_memory_array_increase_by(kt_tacocat_packet_headers_d, sizeof(f_string_map_t), (void **) &set->headers.array, &set->headers.used, &set->headers.size); if (F_status_is_error_not(set->status)) { - set->status = f_memory_array_increase_by(kt_tacocat_packet_prebuffer_d, sizeof(f_char_t), (void **) &set->header.string, &set->header.used, &set->header.size); + set->status = f_memory_array_increase_by(kt_tacocat_packet_prebuffer_d, sizeof(f_char_t), (void **) &set->buffer.string, &set->buffer.used, &set->buffer.size); } if (F_status_is_error_not(set->status)) { set->status = f_memory_array_increase_by(kt_tacocat_packet_headers_d, sizeof(f_abstruse_map_t), (void **) &set->abstruses.array, &set->abstruses.used, &set->abstruses.size); } - if (F_status_is_error_not(set->status)) { - set->status = f_memory_array_increase_by(set->file.size_read + f_fss_payload_object_payload_s.used + f_fss_payload_object_end_s.used + 1, sizeof(f_char_t), (void **) &set->buffer.string, &set->buffer.used, &set->buffer.size); - } - if (F_status_is_error(set->status)) { macro_kt_send_process_handle_error_exit_1(main, f_memory_array_increase_by, kt_tacocat_send_s, set->network, set->status, set->name, set->flag); } @@ -132,6 +130,13 @@ extern "C" { set->abstruses.array[5].value.type = f_abstruse_dynamic_e; set->abstruses.array[5].value.is.a_dynamic = set->name; + // Index 6 is the salt. + set->abstruses.array[6].key = kt_tacocat_salt_s; + set->abstruses.array[6].value.type = f_abstruse_unsigned_e; + set->abstruses.array[6].value.is.a_unsigned = random(); + + set->abstruses.used = 7; + set->status = f_file_open(set->name, F_file_mode_all_r_d, &set->file); if (F_status_is_error(set->status)) { @@ -179,93 +184,80 @@ extern "C" { } set->abstruses.array[3].value.is.a_unsigned = 0; + set->size_total = (f_number_unsigned_t) total; if (total) { - set->abstruses.array[4].value.is.a_unsigned = ((f_number_unsigned_t) total) / set->file.size_read; + if (set->size_total < set->file.size_read) { + set->abstruses.array[2].value.is.a_unsigned = set->size_total; + set->abstruses.array[4].value.is.a_unsigned = 1; + } + else { + set->abstruses.array[2].value.is.a_unsigned = set->file.size_read; + set->abstruses.array[4].value.is.a_unsigned = set->size_total / set->file.size_read; - if (set->abstruses.array[4].value.is.a_unsigned % set->file.size_read) { - ++set->abstruses.array[4].value.is.a_unsigned; + if (set->abstruses.array[4].value.is.a_unsigned % set->file.size_read) { + ++set->abstruses.array[4].value.is.a_unsigned; + } } } else { + set->abstruses.array[2].value.is.a_unsigned = set->size_total; set->abstruses.array[4].value.is.a_unsigned = 1; } - set->flag = kt_tacocat_socket_flag_send_file_e; - } - - if (set->flag == kt_tacocat_socket_flag_send_file_e) { - set->buffer.used = 0; - - set->status = f_string_dynamic_append(f_fss_payload_object_payload_s, &set->buffer); - - if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_payload_object_end_s, &set->buffer); - } - - if (F_status_is_error_not(set->status)) { - set->status = f_file_read_block(set->file, &set->buffer); - } - - if (F_status_is_error(set->status)) { - macro_kt_send_process_handle_error_exit_1(main, f_file_read_block, kt_tacocat_send_file_s, set->network, set->status, set->name, set->flag); - } - - set->abstruses.array[2].value.is.a_unsigned = set->buffer.used - f_fss_payload_object_payload_s.used - f_fss_payload_object_end_s.used; - set->buffer.string[set->buffer.used] = 0; set->flag = kt_tacocat_socket_flag_send_build_e; } if (set->flag == kt_tacocat_socket_flag_send_build_e) { - set->cache.used = 0; - - { - f_state_t state_local = main->setting.state; - state_local.data = &set->write_state; + f_state_t state_local = main->setting.state; + state_local.data = &set->write_state; - fl_fss_payload_header_map(set->abstruses, &set->headers, &state_local); - macro_kt_send_process_handle_error_exit_1(main, fl_fss_payload_header_map, kt_tacocat_send_build_s, set->network, state_local.status, set->name, set->flag); + fl_fss_payload_header_map(set->abstruses, &set->headers, &state_local); + macro_kt_send_process_handle_error_exit_1(main, fl_fss_payload_header_map, kt_tacocat_send_build_s, set->network, state_local.status, set->name, set->flag); - set->flag = kt_tacocat_socket_flag_send_combine_e; - } + set->flag = kt_tacocat_socket_flag_send_header_e; } - if (set->flag == kt_tacocat_socket_flag_send_combine_e) { - set->header.used = 0; + if (set->flag == kt_tacocat_socket_flag_send_header_e) { + // @todo this needs to check the current status, accodingly (for when multiple blocks are being sent). + + // Reserve the FSS Packet header, which will be calculated just before sending. + set->buffer.used = 5; + memset(set->buffer.string, 0, 5); - set->status = f_string_dynamic_append(f_fss_payload_comment_header_begin_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_payload_comment_header_begin_s, &set->buffer); if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_payload_comment_header_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_payload_comment_header_s, &set->buffer); } if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_payload_comment_header_end_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_payload_comment_header_end_s, &set->buffer); } if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_payload_object_header_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_payload_object_header_s, &set->buffer); } if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_payload_object_end_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_payload_object_end_s, &set->buffer); } if (F_status_is_error_not(set->status)) { for (f_number_unsigned_t i = 0; i < set->headers.used; ++i) { - set->status = f_string_dynamic_append(set->headers.array[i].name, &set->header); + set->status = f_string_dynamic_append_nulless(set->headers.array[i].name, &set->buffer); if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_extended_open_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_extended_open_s, &set->buffer); } if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(set->headers.array[i].value, &set->header); + set->status = f_string_dynamic_append_nulless(set->headers.array[i].value, &set->buffer); } if (F_status_is_error_not(set->status)) { - set->status = f_string_dynamic_append(f_fss_extended_close_s, &set->header); + set->status = f_string_dynamic_append_nulless(f_fss_extended_close_s, &set->buffer); } else { break; @@ -273,63 +265,95 @@ extern "C" { } // for } + macro_kt_send_process_handle_error_exit_1(main, f_string_dynamic_append_nulless, kt_tacocat_send_combine_s, set->network, set->status, set->name, set->flag); + + set->flag = kt_tacocat_socket_flag_send_file_e; + } + + if (set->flag == kt_tacocat_socket_flag_send_file_e) { + const f_number_unsigned_t size_header = set->buffer.used; + + set->status = f_string_dynamic_append(f_fss_payload_object_payload_s, &set->buffer); + if (F_status_is_error_not(set->status)) { - macro_kt_send_process_handle_error_exit_1(main, f_string_dynamic_append, kt_tacocat_send_combine_s, set->network, set->status, set->name, set->flag); + set->status = f_string_dynamic_append(f_fss_payload_object_end_s, &set->buffer); } - set->header.string[set->header.used] = 0; - set->flag = kt_tacocat_socket_flag_send_header_e; + macro_kt_send_process_handle_error_exit_1(main, f_string_dynamic_append, kt_tacocat_send_file_s, set->network, set->status, set->name, set->flag); + + // Always reset the seek position in case a retry happened after the file block is read. + { + off_t seeked = 0; + + set->status = f_file_seek(set->file, SEEK_SET, set->size_done, &seeked); + macro_kt_send_process_handle_error_exit_1(main, f_file_seek, kt_tacocat_send_file_s, set->network, set->status, set->name, set->flag); + } + + set->status = f_file_read_block(set->file, &set->buffer); + macro_kt_send_process_handle_error_exit_1(main, f_file_read_block, kt_tacocat_send_file_s, set->network, set->status, set->name, set->flag); + + set->status = f_string_dynamic_terminate_after(&set->buffer); + macro_kt_send_process_handle_error_exit_1(main, f_string_dynamic_terminate_after, kt_tacocat_send_combine_s, set->network, set->status, set->name, set->flag); + + set->flag = kt_tacocat_socket_flag_send_check_e; } - if (set->flag == kt_tacocat_socket_flag_send_header_e) { - size_t written = 0; + if (set->flag == kt_tacocat_socket_flag_send_check_e) { + // @todo this needs to check if the size read has changed and then re-build the header (swap the buffer read block into the cache then rebuild the header with th new size). + //if (set->abstruses.array[2].value.is.a_unsigned < set->file.size_read) { + //} - set->socket.size_write = set->size_done + set->size_block > set->header.used ? set->header.used - set->size_done : set->size_block; + set->flag = kt_tacocat_socket_flag_send_encode_e; + } - set->status = f_socket_write_stream(&set->socket, f_socket_flag_signal_not_e, (void *) (set->header.string + set->size_done), &written); + if (set->flag == kt_tacocat_socket_flag_send_encode_e) { + const f_number_unsigned_t original = set->buffer.used; - macro_kt_send_process_handle_error_exit_1(main, f_socket_write_stream, kt_tacocat_send_header_s, set->network, set->status, set->name, set->flag); + set->buffer.used = 0; - set->size_done += written; + // @todo there will need to be checks for max-size and the payload size, shrinking the payload if the header + payload is too large. + // @todo if the header file is so large that the payload is smaller than a reasonable minumum (say 32 bytes), then this is a problem and throw a too large/small error. + set->status = f_fss_simple_packet_encode(F_fss_simple_packet_endian_d, original, &set->buffer); - // When the buffer is smaller than the read block size, then the entire file should be completely sent. - if (set->size_done >= set->header.used) { - set->size_done = 0; - set->flag = kt_tacocat_socket_flag_send_payload_e; - } - else { - set->status = F_okay; + set->buffer.used = original; - return F_done_not; - } + macro_kt_send_process_handle_error_exit_1(main, f_fss_simple_packet_encode, kt_tacocat_send_payload_s, set->network, set->status, set->name, set->flag); + + set->flag = kt_tacocat_socket_flag_send_packet_e; } - if (set->flag == kt_tacocat_socket_flag_send_payload_e) { + if (set->flag == kt_tacocat_socket_flag_send_packet_e) { size_t written = 0; - set->socket.size_write = set->size_done + set->size_block > set->buffer.used ? set->buffer.used - set->size_done : set->size_block; + { + const size_t original = set->socket.size_write; + + set->socket.size_write = set->buffer.used; - set->status = f_socket_write_stream(&set->socket, f_socket_flag_signal_not_e, (void *) (set->buffer.string + set->size_done), &written); - macro_kt_send_process_handle_error_exit_1(main, f_socket_write_stream, kt_tacocat_send_payload_s, set->network, set->status, set->name, set->flag); + set->status = f_socket_write_stream(&set->socket, f_socket_flag_signal_not_e, (void *) set->buffer.string, &written); + + set->socket.size_write = original; + + macro_kt_send_process_handle_error_exit_1(main, f_socket_write_stream, kt_tacocat_send_payload_s, set->network, set->status, set->name, set->flag); + } + + // @todo handle case when written < set->buffer.used, of which each pass. The entire buffer must be sent. May need another variable for say, set->size_process. set->size_done += written; // When the buffer is smaller than the read block size, then the entire file should be completely sent. if (set->size_done >= set->buffer.used) { - set->size_done = 0; set->flag = kt_tacocat_socket_flag_send_done_e; } else { set->status = F_okay; + set->flag = kt_tacocat_socket_flag_send_header_e; return F_done_not; } } if (set->flag == kt_tacocat_socket_flag_send_done_e) { - set->flag = 0; - set->size_done = 0; - set->status = f_file_close(&set->file); if (F_status_is_error(set->status)) { @@ -344,6 +368,8 @@ extern "C" { f_file_close_id(&set->socket.id_data); + set->flag = 0; + set->size_done = 0; set->socket.id = -1; set->socket.id_data = -1; set->status = F_okay; diff --git a/sources/c/tacocat/main/tacocat.c b/sources/c/tacocat/main/tacocat.c index d8056bc..a0e259a 100644 --- a/sources/c/tacocat/main/tacocat.c +++ b/sources/c/tacocat/main/tacocat.c @@ -39,6 +39,9 @@ extern "C" { return; } + // Establish random seed. + srandom((long) time(0)); + kt_tacocat_process_main(main); if (main->program.signal_received) { -- 1.8.3.1