2015-09-01 23:01:38 +08:00
|
|
|
/****************************************************************************
|
2016-07-05 18:46:22 +08:00
|
|
|
** Copyright (c) 2016, Fougue Ltd. <http://www.fougue.pro>
|
|
|
|
** All rights reserved.
|
2015-09-01 23:01:38 +08:00
|
|
|
**
|
2016-07-05 18:46:22 +08:00
|
|
|
** Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions
|
|
|
|
** are met:
|
2015-09-01 23:01:38 +08:00
|
|
|
**
|
2016-07-05 18:46:22 +08:00
|
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
**
|
|
|
|
** 2. Redistributions in binary form must reproduce the above
|
|
|
|
** copyright notice, this list of conditions and the following
|
|
|
|
** disclaimer in the documentation and/or other materials provided
|
|
|
|
** with the distribution.
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2015-09-01 23:01:38 +08:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <gmio_core/error.h>
|
|
|
|
#include <gmio_stl/stl_io.h>
|
2015-12-04 01:00:25 +08:00
|
|
|
#include <gmio_stl/stl_io_options.h>
|
|
|
|
#include <gmio_stl/stl_mesh.h>
|
|
|
|
#include <gmio_stl/stl_mesh_creator.h>
|
2015-12-10 01:51:03 +08:00
|
|
|
#include <gmio_stl/stl_infos.h>
|
2016-01-13 19:29:44 +08:00
|
|
|
#include <gmio_stl/stl_format.h>
|
2015-09-01 23:01:38 +08:00
|
|
|
|
2015-09-01 23:30:49 +08:00
|
|
|
#include "../commons/benchmark_tools.h"
|
2015-09-01 23:01:38 +08:00
|
|
|
|
2016-03-21 17:18:30 +08:00
|
|
|
#include <stdio.h>
|
2015-09-01 23:01:38 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
2015-12-04 01:00:25 +08:00
|
|
|
struct gmio_stl_triangle;
|
|
|
|
|
|
|
|
struct my_igeom
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
|
|
|
uint32_t facet_count;
|
2015-12-04 01:00:25 +08:00
|
|
|
};
|
2015-09-01 23:01:38 +08:00
|
|
|
|
|
|
|
static void dummy_process_triangle(
|
|
|
|
void* cookie,
|
|
|
|
uint32_t triangle_id,
|
2015-12-04 01:00:25 +08:00
|
|
|
const struct gmio_stl_triangle* triangle)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2015-12-04 01:00:25 +08:00
|
|
|
struct my_igeom* my_igeom = (struct my_igeom*)(cookie);
|
2015-09-23 21:30:17 +08:00
|
|
|
GMIO_UNUSED(triangle_id);
|
|
|
|
GMIO_UNUSED(triangle);
|
2015-09-01 23:01:38 +08:00
|
|
|
if (my_igeom != NULL)
|
|
|
|
++(my_igeom->facet_count);
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:37:45 +08:00
|
|
|
static void bmk_gmio_stl_read(const void* filepath)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2015-12-04 01:00:25 +08:00
|
|
|
struct my_igeom cookie = {0};
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stl_mesh_creator mesh_creator = {0};
|
2015-09-01 23:01:38 +08:00
|
|
|
int error = GMIO_ERROR_OK;
|
|
|
|
|
2016-01-29 19:47:01 +08:00
|
|
|
mesh_creator.cookie = &cookie;
|
|
|
|
mesh_creator.func_add_triangle = dummy_process_triangle;
|
2016-03-01 21:50:31 +08:00
|
|
|
error = gmio_stl_read_file(filepath, &mesh_creator, NULL);
|
2015-09-01 23:01:38 +08:00
|
|
|
if (error != GMIO_ERROR_OK)
|
2016-03-21 17:18:30 +08:00
|
|
|
fprintf(stderr, "gmio error: 0x%X\n", error);
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
|
2015-12-04 01:00:25 +08:00
|
|
|
static enum gmio_endianness to_byte_order(enum gmio_stl_format format)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
case GMIO_STL_FORMAT_BINARY_LE:
|
|
|
|
return GMIO_ENDIANNESS_LITTLE;
|
|
|
|
case GMIO_STL_FORMAT_BINARY_BE:
|
|
|
|
return GMIO_ENDIANNESS_BIG;
|
|
|
|
case GMIO_STL_FORMAT_ASCII:
|
|
|
|
case GMIO_STL_FORMAT_UNKNOWN:
|
|
|
|
return GMIO_ENDIANNESS_HOST;
|
|
|
|
}
|
|
|
|
return GMIO_ENDIANNESS_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum { STL_TRIANGLE_ARRAY_SIZE = 512 };
|
2015-12-04 01:00:25 +08:00
|
|
|
struct stl_readwrite_conv
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stream stream;
|
2015-12-04 01:03:46 +08:00
|
|
|
struct gmio_streampos out_stream_pos_begin;
|
2015-12-04 01:00:25 +08:00
|
|
|
enum gmio_stl_format in_format;
|
|
|
|
enum gmio_stl_format out_format;
|
|
|
|
struct gmio_stl_triangle triangle_array[STL_TRIANGLE_ARRAY_SIZE];
|
2015-09-01 23:01:38 +08:00
|
|
|
unsigned triangle_pos;
|
|
|
|
uint32_t total_triangle_count;
|
2015-12-04 01:00:25 +08:00
|
|
|
};
|
2015-09-01 23:01:38 +08:00
|
|
|
|
2016-02-01 23:53:45 +08:00
|
|
|
static void readwrite_begin_solid(
|
|
|
|
void* cookie, const struct gmio_stl_mesh_creator_infos* infos)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2015-12-04 01:00:25 +08:00
|
|
|
struct stl_readwrite_conv* rw_conv = (struct stl_readwrite_conv*)cookie;
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stream* stream = &rw_conv->stream;
|
2015-09-01 23:01:38 +08:00
|
|
|
|
2016-02-01 23:53:45 +08:00
|
|
|
if (infos->format == GMIO_STL_FORMAT_ASCII) {
|
|
|
|
if (rw_conv->out_format == GMIO_STL_FORMAT_ASCII) {
|
|
|
|
stream->func_write(stream->cookie, "solid ", 1, 6);
|
|
|
|
stream->func_write(
|
|
|
|
stream->cookie,
|
|
|
|
infos->stla_solid_name,
|
|
|
|
sizeof(char),
|
|
|
|
strlen(infos->stla_solid_name));
|
|
|
|
stream->func_write(stream->cookie, "\n", 1, 1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* For binary STL, facet count <- 0 because it cannot be known at this
|
|
|
|
* point. Header will be correctly rewritten at the end of the read
|
|
|
|
* procedure (in gmio_stl_mesh_creator::func_end_solid() callback)
|
|
|
|
*/
|
|
|
|
const enum gmio_endianness byte_order = to_byte_order(rw_conv->out_format);
|
2016-03-11 19:43:30 +08:00
|
|
|
gmio_stlb_header_write(stream, byte_order, NULL, 0);
|
2016-02-01 23:53:45 +08:00
|
|
|
}
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
else {
|
2016-02-01 23:53:45 +08:00
|
|
|
if (rw_conv->out_format == GMIO_STL_FORMAT_ASCII) {
|
|
|
|
stream->func_write(stream->cookie, "solid\n", 1, 6);
|
|
|
|
}
|
|
|
|
else {
|
2016-03-11 19:43:30 +08:00
|
|
|
gmio_stlb_header_write(
|
2016-02-01 23:53:45 +08:00
|
|
|
stream,
|
|
|
|
to_byte_order(rw_conv->out_format),
|
|
|
|
infos->stlb_header,
|
|
|
|
infos->stlb_triangle_count);
|
|
|
|
}
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void readwrite_get_triangle(
|
2015-12-04 01:00:25 +08:00
|
|
|
const void* cookie, uint32_t tri_id, struct gmio_stl_triangle* triangle)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2015-12-04 17:32:11 +08:00
|
|
|
const struct gmio_stl_triangle* tri_array =
|
|
|
|
(const struct gmio_stl_triangle*)cookie;
|
2015-09-01 23:01:38 +08:00
|
|
|
*triangle = tri_array[tri_id];
|
|
|
|
}
|
|
|
|
|
2015-12-04 01:00:25 +08:00
|
|
|
static void stl_readwrite_flush_triangles(struct stl_readwrite_conv* rw_conv)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stl_mesh mesh = {0};
|
|
|
|
struct gmio_stl_write_options options = {0};
|
|
|
|
|
|
|
|
mesh.cookie = &rw_conv->triangle_array[0];
|
|
|
|
mesh.triangle_count = rw_conv->triangle_pos;
|
|
|
|
mesh.func_get_triangle = &readwrite_get_triangle;
|
|
|
|
|
|
|
|
options.stl_write_triangles_only = true;
|
|
|
|
options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE;
|
|
|
|
options.stla_float32_prec = 6;
|
|
|
|
|
2016-03-01 21:50:31 +08:00
|
|
|
gmio_stl_write(rw_conv->out_format, &rw_conv->stream, &mesh, &options);
|
2015-09-01 23:01:38 +08:00
|
|
|
rw_conv->triangle_pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void readwrite_add_triangle(
|
2015-12-04 01:00:25 +08:00
|
|
|
void* cookie, uint32_t tri_id, const struct gmio_stl_triangle* triangle)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
2015-12-04 01:00:25 +08:00
|
|
|
struct stl_readwrite_conv* rw_conv = (struct stl_readwrite_conv*)cookie;
|
2015-09-01 23:01:38 +08:00
|
|
|
if (rw_conv->triangle_pos >= STL_TRIANGLE_ARRAY_SIZE)
|
|
|
|
stl_readwrite_flush_triangles(rw_conv);
|
|
|
|
rw_conv->triangle_array[rw_conv->triangle_pos] = *triangle;
|
|
|
|
++(rw_conv->triangle_pos);
|
|
|
|
rw_conv->total_triangle_count = tri_id + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void readwrite_end_solid(void* cookie)
|
|
|
|
{
|
2015-12-04 01:00:25 +08:00
|
|
|
struct stl_readwrite_conv* rw_conv = (struct stl_readwrite_conv*)cookie;
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stream* stream = &rw_conv->stream;
|
2015-09-01 23:01:38 +08:00
|
|
|
if (rw_conv->triangle_pos != 0)
|
|
|
|
stl_readwrite_flush_triangles(rw_conv);
|
|
|
|
if (rw_conv->out_format == GMIO_STL_FORMAT_ASCII) {
|
|
|
|
stream->func_write(stream->cookie, "endsolid", 1, 8);
|
|
|
|
}
|
|
|
|
else if (rw_conv->in_format == GMIO_STL_FORMAT_ASCII) {
|
2015-12-04 01:00:25 +08:00
|
|
|
const enum gmio_endianness byte_order = to_byte_order(rw_conv->out_format);
|
2015-09-01 23:01:38 +08:00
|
|
|
/* The total facet count has to be written because it wasn't known at
|
|
|
|
* the beginning of the read procedure */
|
2015-09-23 00:16:39 +08:00
|
|
|
stream->func_set_pos(stream->cookie, &rw_conv->out_stream_pos_begin);
|
2016-03-11 19:43:30 +08:00
|
|
|
gmio_stlb_header_write(
|
2015-09-01 23:01:38 +08:00
|
|
|
stream, byte_order, NULL, rw_conv->total_triangle_count);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:37:45 +08:00
|
|
|
static void bmk_gmio_stl_readwrite_conv(const void* filepath)
|
2015-09-01 23:01:38 +08:00
|
|
|
{
|
|
|
|
FILE* infile = fopen(filepath, "rb");
|
|
|
|
FILE* outfile = fopen("_readwrite_conv.stl", "wb");
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stream instream = {0};
|
2015-12-04 01:00:25 +08:00
|
|
|
struct stl_readwrite_conv rw_conv = {0};
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stl_mesh_creator mesh_creator = {0};
|
2015-09-01 23:01:38 +08:00
|
|
|
int error = GMIO_ERROR_OK;
|
|
|
|
|
|
|
|
/* rw_conv.out_format = GMIO_STL_FORMAT_BINARY_LE; */
|
|
|
|
rw_conv.out_format = GMIO_STL_FORMAT_ASCII;
|
|
|
|
|
|
|
|
if (infile != NULL) {
|
2016-01-29 19:47:01 +08:00
|
|
|
instream = gmio_stream_stdio(infile);
|
2016-03-11 19:43:30 +08:00
|
|
|
rw_conv.in_format = gmio_stl_format_probe(&instream);
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
if (outfile != NULL) {
|
2016-01-29 19:47:01 +08:00
|
|
|
rw_conv.stream = gmio_stream_stdio(outfile);
|
|
|
|
rw_conv.stream.func_get_pos(
|
|
|
|
rw_conv.stream.cookie,
|
2015-09-23 00:16:39 +08:00
|
|
|
&rw_conv.out_stream_pos_begin);
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
|
2016-01-29 19:47:01 +08:00
|
|
|
mesh_creator.cookie = &rw_conv;
|
2016-02-01 23:53:45 +08:00
|
|
|
mesh_creator.func_begin_solid = &readwrite_begin_solid;
|
2016-01-29 19:47:01 +08:00
|
|
|
mesh_creator.func_add_triangle = &readwrite_add_triangle;
|
|
|
|
mesh_creator.func_end_solid = &readwrite_end_solid;
|
2015-09-01 23:01:38 +08:00
|
|
|
|
2016-03-01 21:50:31 +08:00
|
|
|
error = gmio_stl_read(&instream, &mesh_creator, NULL);
|
2015-09-01 23:01:38 +08:00
|
|
|
|
|
|
|
if (error != GMIO_ERROR_OK)
|
2016-03-21 17:18:30 +08:00
|
|
|
fprintf(stderr, "gmio error: 0x%X\n", error);
|
2015-12-10 01:51:03 +08:00
|
|
|
|
|
|
|
fclose(infile);
|
|
|
|
fclose(outfile);
|
|
|
|
}
|
|
|
|
|
2016-07-13 17:46:28 +08:00
|
|
|
void bmk_gmio_stl_infos_probe_all(const void* filepath)
|
2015-12-10 01:51:03 +08:00
|
|
|
{
|
2016-01-27 00:03:58 +08:00
|
|
|
static bool already_exec = false;
|
2015-12-10 01:51:03 +08:00
|
|
|
FILE* file = fopen(filepath, "rb");
|
|
|
|
|
|
|
|
if (file != NULL) {
|
2016-03-01 21:50:31 +08:00
|
|
|
struct gmio_stream stream = gmio_stream_stdio(file);
|
2016-01-29 19:47:01 +08:00
|
|
|
struct gmio_stl_infos infos = {0};
|
2016-07-13 17:46:28 +08:00
|
|
|
gmio_stl_infos_probe(&infos, &stream, GMIO_STL_INFO_FLAG_ALL, NULL);
|
2016-01-13 19:29:44 +08:00
|
|
|
|
2015-12-10 01:51:03 +08:00
|
|
|
if (!already_exec) {
|
2016-07-13 17:46:28 +08:00
|
|
|
printf("stl_infos_probe(ALL)\n"
|
2016-01-13 19:29:44 +08:00
|
|
|
" File: %s\n"
|
|
|
|
" Size: %uKo\n"
|
|
|
|
" Facets: %u\n",
|
|
|
|
(const char*)filepath,
|
2016-01-29 19:47:01 +08:00
|
|
|
infos.size / 1024,
|
|
|
|
infos.facet_count);
|
2016-03-08 17:29:49 +08:00
|
|
|
if (infos.format == GMIO_STL_FORMAT_ASCII)
|
2016-01-29 19:47:01 +08:00
|
|
|
printf(" [STLA]Solid name: %s\n", infos.stla_solidname);
|
2016-03-08 17:29:49 +08:00
|
|
|
else if (infos.format & GMIO_STL_FORMAT_TAG_BINARY)
|
2016-01-29 19:47:01 +08:00
|
|
|
printf(" [STLB]Header: %80.80s\n", infos.stlb_header.data);
|
2015-12-10 01:51:03 +08:00
|
|
|
}
|
2016-01-27 00:03:58 +08:00
|
|
|
already_exec = true;
|
2015-12-10 01:51:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
|
2016-07-13 17:46:28 +08:00
|
|
|
void bmk_gmio_stl_infos_probe_size(const void* filepath)
|
2016-02-03 18:43:52 +08:00
|
|
|
{
|
|
|
|
static bool already_exec = false;
|
|
|
|
FILE* file = fopen(filepath, "rb");
|
|
|
|
|
|
|
|
if (file != NULL) {
|
2016-03-01 21:50:31 +08:00
|
|
|
struct gmio_stream stream = gmio_stream_stdio(file);
|
2016-02-03 18:43:52 +08:00
|
|
|
struct gmio_stl_infos infos = {0};
|
2016-07-13 17:46:28 +08:00
|
|
|
gmio_stl_infos_probe(&infos, &stream, GMIO_STL_INFO_FLAG_SIZE, NULL);
|
2016-02-03 18:43:52 +08:00
|
|
|
|
|
|
|
if (!already_exec) {
|
2016-07-13 17:46:28 +08:00
|
|
|
printf("stl_infos_probe(SIZE)\n"
|
2016-02-03 18:43:52 +08:00
|
|
|
" File: %s\n"
|
|
|
|
" Size: %uKo\n",
|
|
|
|
(const char*)filepath,
|
|
|
|
infos.size / 1024);
|
|
|
|
}
|
|
|
|
already_exec = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
}
|
|
|
|
|
2015-09-01 23:01:38 +08:00
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
|
|
|
if (argc > 1) {
|
2015-09-23 21:30:17 +08:00
|
|
|
const char* filepath = argv[1];
|
|
|
|
|
|
|
|
/* Declare benchmarks */
|
2015-12-04 01:00:25 +08:00
|
|
|
struct benchmark_cmp_arg cmp_args[] = {
|
2015-09-23 21:30:17 +08:00
|
|
|
{ "read_file()",
|
|
|
|
bmk_gmio_stl_read, NULL,
|
|
|
|
NULL, NULL },
|
|
|
|
{ "readwrite_conv()",
|
|
|
|
bmk_gmio_stl_readwrite_conv, NULL,
|
|
|
|
NULL, NULL },
|
2016-07-13 17:46:28 +08:00
|
|
|
{ "stl_infos_probe(ALL)",
|
|
|
|
bmk_gmio_stl_infos_probe_all, NULL,
|
2016-02-03 18:43:52 +08:00
|
|
|
NULL, NULL },
|
2016-07-13 17:46:28 +08:00
|
|
|
{ "stl_infos_probe(size)",
|
|
|
|
bmk_gmio_stl_infos_probe_size, NULL,
|
2015-12-10 01:51:03 +08:00
|
|
|
NULL, NULL },
|
2015-09-23 21:30:17 +08:00
|
|
|
{0}
|
|
|
|
};
|
2016-01-13 19:29:44 +08:00
|
|
|
const size_t cmp_count = GMIO_ARRAY_SIZE(cmp_args) - 1;
|
|
|
|
struct benchmark_cmp_result cmp_res[GMIO_ARRAY_SIZE(cmp_args)] = {0};
|
2015-12-04 01:00:25 +08:00
|
|
|
struct benchmark_cmp_result_array res_array = {0};
|
|
|
|
const struct benchmark_cmp_result_header header = { "gmio", NULL };
|
2015-09-23 21:30:17 +08:00
|
|
|
|
2015-12-10 01:51:03 +08:00
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
for (i = 0; i < cmp_count; ++i)
|
2016-01-13 00:37:45 +08:00
|
|
|
cmp_args[i].func1_arg = filepath;
|
2015-12-10 01:51:03 +08:00
|
|
|
}
|
2015-09-23 21:30:17 +08:00
|
|
|
|
|
|
|
res_array.ptr = &cmp_res[0];
|
|
|
|
res_array.count = cmp_count;
|
|
|
|
|
|
|
|
/* Execute benchmarks */
|
2016-01-13 19:29:44 +08:00
|
|
|
benchmark_cmp_batch(5, cmp_args, cmp_res, NULL, NULL);
|
2015-09-23 21:30:17 +08:00
|
|
|
|
|
|
|
/* Print results */
|
|
|
|
benchmark_print_results(
|
|
|
|
BENCHMARK_PRINT_FORMAT_MARKDOWN, header, res_array);
|
2015-09-01 23:01:38 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|