From 319e88585a25c57164f28f084ef8d75fbd9c34ed Mon Sep 17 00:00:00 2001 From: Ruslan Mustakov Date: Sat, 17 Feb 2018 17:40:28 +0700 Subject: [PATCH 1/9] Fix AsyncFile open flags. Fixes #5531 --- lib/pure/asyncfile.nim | 13 ++++++------- lib/windows/winlean.nim | 1 + tests/async/tasyncfile.nim | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 6ce9e8f75..97bec2815 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -50,22 +50,21 @@ when defined(windows) or defined(nimdoc): case mode of fmRead, fmReadWriteExisting: OPEN_EXISTING - of fmAppend, fmReadWrite, fmWrite: - if fileExists(filename): - OPEN_EXISTING - else: - CREATE_NEW + of fmReadWrite, fmWrite: + CREATE_ALWAYS + of fmAppend: + OPEN_ALWAYS else: proc getPosixFlags(mode: FileMode): cint = case mode of fmRead: result = O_RDONLY of fmWrite: - result = O_WRONLY or O_CREAT + result = O_WRONLY or O_CREAT or O_TRUNC of fmAppend: result = O_WRONLY or O_CREAT or O_APPEND of fmReadWrite: - result = O_RDWR or O_CREAT + result = O_RDWR or O_CREAT or O_TRUNC of fmReadWriteExisting: result = O_RDWR result = result or O_NONBLOCK diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index bd6e58a10..b2c1cc1f5 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -666,6 +666,7 @@ const CREATE_ALWAYS* = 2'i32 CREATE_NEW* = 1'i32 OPEN_EXISTING* = 3'i32 + OPEN_ALWAYS* = 4'i32 FILE_BEGIN* = 0'i32 INVALID_SET_FILE_POINTER* = -1'i32 NO_ERROR* = 0'i32 diff --git a/tests/async/tasyncfile.nim b/tests/async/tasyncfile.nim index 6c0725c88..aa7f03ab1 100644 --- a/tests/async/tasyncfile.nim +++ b/tests/async/tasyncfile.nim @@ -41,11 +41,11 @@ proc main() {.async.} = await file.write("test2") file.close() file = openAsync(fn, fmWrite) - await file.write("test3") + await file.write("t3") file.close() file = openAsync(fn, fmRead) let data = await file.readAll() - doAssert data == "test3" + doAssert data == "t3" file.close() From fe204a5e73e6ed7edba9edc36f18a10b9dc311ca Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 17 Feb 2018 17:46:22 +0000 Subject: [PATCH 2/9] Fixes #5786. --- changelog.md | 3 +- lib/wrappers/libsvm.nim | 117 ---------------------------------------- web/website.ini | 1 - 3 files changed, 2 insertions(+), 119 deletions(-) delete mode 100644 lib/wrappers/libsvm.nim diff --git a/changelog.md b/changelog.md index 0f6be33c3..5c72495d2 100644 --- a/changelog.md +++ b/changelog.md @@ -104,6 +104,7 @@ This now needs to be written as: - [``poly``](https://github.com/lcrees/polynumeric) - [``pdcurses``](https://github.com/lcrees/pdcurses) - [``romans``](https://github.com/lcrees/romans) + - [``libsvm``](https://github.com/nim-lang/libsvm_legacy) - Added ``system.runnableExamples`` to make examples in Nim's documentation easier to write and test. The examples are tested as the last step of @@ -263,4 +264,4 @@ bar() import std / [strutils, os, osproc] import someNimblePackage / [strutils, os] -``` +``` \ No newline at end of file diff --git a/lib/wrappers/libsvm.nim b/lib/wrappers/libsvm.nim deleted file mode 100644 index ac5889410..000000000 --- a/lib/wrappers/libsvm.nim +++ /dev/null @@ -1,117 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module is a low level wrapper for `libsvm`:idx:. - -{.deadCodeElim: on.} -const - LIBSVM_VERSION* = 312 - -when defined(windows): - const svmdll* = "libsvm.dll" -elif defined(macosx): - const svmdll* = "libsvm.dylib" -else: - const svmdll* = "libsvm.so" - -type - Node*{.pure, final.} = object - index*: cint - value*: cdouble - - Problem*{.pure, final.} = object - L*: cint - y*: ptr cdouble - x*: ptr ptr Node - - Type*{.size: sizeof(cint).} = enum - C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR - - KernelType*{.size: sizeof(cint).} = enum - LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED - - Parameter*{.pure, final.} = object - typ*: Type - kernelType*: KernelType - degree*: cint # for poly - gamma*: cdouble # for poly/rbf/sigmoid - coef0*: cdouble # for poly/sigmoid - # these are for training only - cache_size*: cdouble # in MB - eps*: cdouble # stopping criteria - C*: cdouble # for C_SVC, EPSILON_SVR and NU_SVR - nr_weight*: cint # for C_SVC - weight_label*: ptr cint # for C_SVC - weight*: ptr cdouble # for C_SVC - nu*: cdouble # for NU_SVC, ONE_CLASS, and NU_SVR - p*: cdouble # for EPSILON_SVR - shrinking*: cint # use the shrinking heuristics - probability*: cint # do probability estimates -{.deprecated: [Tnode: Node, Tproblem: Problem, Ttype: Type, - TKernelType: KernelType, Tparameter: Parameter].} - -# -# svm_model -# - -type - Model*{.pure, final.} = object - param*: Parameter # parameter - nr_class*: cint # number of classes, = 2 in regression/one class svm - L*: cint # total #SV - SV*: ptr ptr Node # SVs (SV[l]) - sv_coef*: ptr ptr cdouble # coefficients for SVs in decision functions (sv_coef[k-1][l]) - rho*: ptr cdouble # constants in decision functions (rho[k*(k-1)/2]) - probA*: ptr cdouble # pariwise probability information - probB*: ptr cdouble # for classification only - label*: ptr cint # label of each class (label[k]) - nSV*: ptr cint # number of SVs for each class (nSV[k]) - # nSV[0] + nSV[1] + ... + nSV[k-1] = l - # XXX - free_sv*: cint # 1 if svm_model is created by svm_load_model - # 0 if svm_model is created by svm_train -{.deprecated: [TModel: Model].} - -proc train*(prob: ptr Problem, param: ptr Parameter): ptr Model{.cdecl, - importc: "svm_train", dynlib: svmdll.} -proc cross_validation*(prob: ptr Problem, param: ptr Parameter, nr_fold: cint, - target: ptr cdouble){.cdecl, - importc: "svm_cross_validation", dynlib: svmdll.} -proc save_model*(model_file_name: cstring, model: ptr Model): cint{.cdecl, - importc: "svm_save_model", dynlib: svmdll.} -proc load_model*(model_file_name: cstring): ptr Model{.cdecl, - importc: "svm_load_model", dynlib: svmdll.} -proc get_svm_type*(model: ptr Model): cint{.cdecl, importc: "svm_get_svm_type", - dynlib: svmdll.} -proc get_nr_class*(model: ptr Model): cint{.cdecl, importc: "svm_get_nr_class", - dynlib: svmdll.} -proc get_labels*(model: ptr Model, label: ptr cint){.cdecl, - importc: "svm_get_labels", dynlib: svmdll.} -proc get_svr_probability*(model: ptr Model): cdouble{.cdecl, - importc: "svm_get_svr_probability", dynlib: svmdll.} -proc predict_values*(model: ptr Model, x: ptr Node, dec_values: ptr cdouble): cdouble{. - cdecl, importc: "svm_predict_values", dynlib: svmdll.} -proc predict*(model: ptr Model, x: ptr Node): cdouble{.cdecl, - importc: "svm_predict", dynlib: svmdll.} -proc predict_probability*(model: ptr Model, x: ptr Node, - prob_estimates: ptr cdouble): cdouble{.cdecl, - importc: "svm_predict_probability", dynlib: svmdll.} -proc free_model_content*(model_ptr: ptr Model){.cdecl, - importc: "svm_free_model_content", dynlib: svmdll.} -proc free_and_destroy_model*(model_ptr_ptr: ptr ptr Model){.cdecl, - importc: "svm_free_and_destroy_model", dynlib: svmdll.} -proc destroy_param*(param: ptr Parameter){.cdecl, importc: "svm_destroy_param", - dynlib: svmdll.} -proc check_parameter*(prob: ptr Problem, param: ptr Parameter): cstring{. - cdecl, importc: "svm_check_parameter", dynlib: svmdll.} -proc check_probability_model*(model: ptr Model): cint{.cdecl, - importc: "svm_check_probability_model", dynlib: svmdll.} - -proc set_print_string_function*(print_func: proc (arg: cstring) {.cdecl.}){. - cdecl, importc: "svm_set_print_string_function", dynlib: svmdll.} diff --git a/web/website.ini b/web/website.ini index 7edaaa712..17ff5d644 100644 --- a/web/website.ini +++ b/web/website.ini @@ -80,4 +80,3 @@ webdoc: "wrappers/openssl" webdoc: "wrappers/joyent_http_parser" webdoc: "posix/posix;wrappers/odbcsql" -webdoc: "wrappers/libsvm.nim" From 5476a82c47413d78b09e07abd07ba78d1d560dc3 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 17 Feb 2018 17:53:32 +0000 Subject: [PATCH 3/9] Move joyent_http_parser out of stdlib. --- changelog.md | 1 + doc/lib.rst | 11 ---- lib/wrappers/joyent_http_parser.nim | 93 ----------------------------- web/website.ini | 1 - 4 files changed, 1 insertion(+), 105 deletions(-) delete mode 100644 lib/wrappers/joyent_http_parser.nim diff --git a/changelog.md b/changelog.md index 5c72495d2..73d7e7d9b 100644 --- a/changelog.md +++ b/changelog.md @@ -105,6 +105,7 @@ This now needs to be written as: - [``pdcurses``](https://github.com/lcrees/pdcurses) - [``romans``](https://github.com/lcrees/romans) - [``libsvm``](https://github.com/nim-lang/libsvm_legacy) + - [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser) - Added ``system.runnableExamples`` to make examples in Nim's documentation easier to write and test. The examples are tested as the last step of diff --git a/doc/lib.rst b/doc/lib.rst index 0932b25e4..2e963451d 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -557,21 +557,10 @@ Database support Network Programming and Internet Protocols ------------------------------------------ -* `joyent_http_parser `_ - Wrapper for the joyent's high-performance HTTP parser. - * `openssl `_ Wrapper for OpenSSL. - -Scientific computing --------------------- - -* `libsvm `_ - Low level wrapper for `lib svm `_. - - Nimble ====== diff --git a/lib/wrappers/joyent_http_parser.nim b/lib/wrappers/joyent_http_parser.nim deleted file mode 100644 index f7412d2b8..000000000 --- a/lib/wrappers/joyent_http_parser.nim +++ /dev/null @@ -1,93 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -type - csize = int - - HttpDataProc* = proc (a2: ptr HttpParser, at: cstring, length: csize): cint {.cdecl.} - HttpProc* = proc (a2: ptr HttpParser): cint {.cdecl.} - - HttpMethod* = enum - HTTP_DELETE = 0, HTTP_GET, HTTP_HEAD, HTTP_POST, HTTP_PUT, HTTP_CONNECT, - HTTP_OPTIONS, HTTP_TRACE, HTTP_COPY, HTTP_LOCK, HTTP_MKCOL, HTTP_MOVE, - HTTP_PROPFIND, HTTP_PROPPATCH, HTTP_UNLOCK, HTTP_REPORT, HTTP_MKACTIVITY, - HTTP_CHECKOUT, HTTP_MERGE, HTTP_MSEARCH, HTTP_NOTIFY, HTTP_SUBSCRIBE, - HTTP_UNSUBSCRIBE, HTTP_PATCH - - HttpParserType* = enum - HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH - - ParserFlag* = enum - F_CHUNKED = 1 shl 0, - F_CONNECTION_KEEP_ALIVE = 1 shl 1, - F_CONNECTION_CLOSE = 1 shl 2, - F_TRAILING = 1 shl 3, - F_UPGRADE = 1 shl 4, - F_SKIPBODY = 1 shl 5 - - HttpErrNo* = enum - HPE_OK, HPE_CB_message_begin, HPE_CB_path, HPE_CB_query_string, HPE_CB_url, - HPE_CB_fragment, HPE_CB_header_field, HPE_CB_header_value, - HPE_CB_headers_complete, HPE_CB_body, HPE_CB_message_complete, - HPE_INVALID_EOF_STATE, HPE_HEADER_OVERFLOW, HPE_CLOSED_CONNECTION, - HPE_INVALID_VERSION, HPE_INVALID_STATUS, HPE_INVALID_METHOD, - HPE_INVALID_URL, HPE_INVALID_HOST, HPE_INVALID_PORT, HPE_INVALID_PATH, - HPE_INVALID_QUERY_STRING, HPE_INVALID_FRAGMENT, HPE_LF_EXPECTED, - HPE_INVALID_HEADER_TOKEN, HPE_INVALID_CONTENT_LENGTH, - HPE_INVALID_CHUNK_SIZE, HPE_INVALID_CONSTANT, HPE_INVALID_INTERNAL_STATE, - HPE_STRICT, HPE_UNKNOWN - - HttpParser*{.pure, final, importc: "http_parser", header: "http_parser.h".} = object - typ {.importc: "type".}: char - flags {.importc: "flags".}: char - state*{.importc: "state".}: char - header_state*{.importc: "header_state".}: char - index*{.importc: "index".}: char - nread*{.importc: "nread".}: cint - content_length*{.importc: "content_length".}: int64 - http_major*{.importc: "http_major".}: cshort - http_minor*{.importc: "http_minor".}: cshort - status_code*{.importc: "status_code".}: cshort - http_method*{.importc: "method".}: cshort - http_errno_bits {.importc: "http_errno".}: char - upgrade {.importc: "upgrade".}: bool - data*{.importc: "data".}: pointer - - HttpParserSettings*{.pure, final, importc: "http_parser_settings", header: "http_parser.h".} = object - on_message_begin*{.importc: "on_message_begin".}: HttpProc - on_url*{.importc: "on_url".}: HttpDataProc - on_header_field*{.importc: "on_header_field".}: HttpDataProc - on_header_value*{.importc: "on_header_value".}: HttpDataProc - on_headers_complete*{.importc: "on_headers_complete".}: HttpProc - on_body*{.importc: "on_body".}: HttpDataProc - on_message_complete*{.importc: "on_message_complete".}: HttpProc -{.deprecated: [THttpMethod: HttpMethod, THttpParserType: HttpParserType, - TParserFlag: ParserFlag, THttpErrNo: HttpErrNo, - THttpParser: HttpParser, THttpParserSettings: HttpParserSettings].} - -proc http_parser_init*(parser: var HttpParser, typ: HttpParserType){. - importc: "http_parser_init", header: "http_parser.h".} - -proc http_parser_execute*(parser: var HttpParser, - settings: var HttpParserSettings, data: cstring, - len: csize): csize {. - importc: "http_parser_execute", header: "http_parser.h".} - -proc http_should_keep_alive*(parser: var HttpParser): cint{. - importc: "http_should_keep_alive", header: "http_parser.h".} - -proc http_method_str*(m: HttpMethod): cstring{. - importc: "http_method_str", header: "http_parser.h".} - -proc http_errno_name*(err: HttpErrNo): cstring{. - importc: "http_errno_name", header: "http_parser.h".} - -proc http_errno_description*(err: HttpErrNo): cstring{. - importc: "http_errno_description", header: "http_parser.h".} - diff --git a/web/website.ini b/web/website.ini index 17ff5d644..9dc5949a0 100644 --- a/web/website.ini +++ b/web/website.ini @@ -77,6 +77,5 @@ webdoc: "wrappers/mysql;wrappers/iup" webdoc: "wrappers/sqlite3;wrappers/postgres;wrappers/tinyc;wrappers/odbcsql" webdoc: "wrappers/pcre" webdoc: "wrappers/openssl" -webdoc: "wrappers/joyent_http_parser" webdoc: "posix/posix;wrappers/odbcsql" From 63bc046ddf2a2ff6694d0a3757ef28ec4ca7b5f3 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 17 Feb 2018 19:36:06 +0000 Subject: [PATCH 4/9] Fixes #7227. --- lib/pure/net.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ba964c39e..99d868847 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -64,6 +64,9 @@ ## socket.acceptAddr(client, address) ## echo("Client connected from: ", address) ## +## **Note:** The ``client`` variable is initialised with ``new Socket`` **not** +## ``newSocket()``. The difference is that the latter creates a new file +## descriptor. {.deadCodeElim: on.} import nativesockets, os, strutils, parseutils, times, sets, options @@ -753,6 +756,8 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string, ## flag is specified then this error will not be raised and instead ## accept will be called again. assert(client != nil) + assert client.fd.int <= 0, "Client socket needs to be initialised with " & + "`new`, not `newSocket`." let ret = accept(server.fd) let sock = ret[0] From fe6fddb9d83c099ee407bced8d59e11e351ecd58 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 17 Feb 2018 22:15:28 +0000 Subject: [PATCH 5/9] Fixes #4265. --- changelog.md | 2 ++ lib/pure/strtabs.nim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 73d7e7d9b..194e098a8 100644 --- a/changelog.md +++ b/changelog.md @@ -257,6 +257,8 @@ bar() - The ``securehash`` module is now deprecated. Instead import ``std / sha1``. - ``db_mysql`` module: ``DbConn`` is now a ``distinct`` type that doesn't expose the details of the underlying ``PMySQL`` type. +- ``strtabs.getOrDefault`` now returns ``nil`` instead of ``""``. See + [#4265](https://github.com/nim-lang/Nim/issues/4265) for more info. - Standard library modules can now also be imported via the ``std`` pseudo-directory. This is useful in order to distinguish between standard library and nimble package imports: diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 75c5e171d..942c2eb72 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -129,7 +129,7 @@ proc mget*(t: StringTableRef, key: string): var string {.deprecated.} = ## ``KeyError`` exception is raised. Use ```[]``` instead. get(t, key) -proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string = +proc getOrDefault*(t: StringTableRef; key: string, default: string = nil): string = var index = rawGet(t, key) if index >= 0: result = t.data[index].val else: result = default From c74a65662a052b01cf5946133dc7d77af20650da Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 17 Feb 2018 22:24:43 +0000 Subject: [PATCH 6/9] Revert "Fixes #4265." This reverts commit fe6fddb9d83c099ee407bced8d59e11e351ecd58. --- changelog.md | 2 -- lib/pure/strtabs.nim | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 194e098a8..73d7e7d9b 100644 --- a/changelog.md +++ b/changelog.md @@ -257,8 +257,6 @@ bar() - The ``securehash`` module is now deprecated. Instead import ``std / sha1``. - ``db_mysql`` module: ``DbConn`` is now a ``distinct`` type that doesn't expose the details of the underlying ``PMySQL`` type. -- ``strtabs.getOrDefault`` now returns ``nil`` instead of ``""``. See - [#4265](https://github.com/nim-lang/Nim/issues/4265) for more info. - Standard library modules can now also be imported via the ``std`` pseudo-directory. This is useful in order to distinguish between standard library and nimble package imports: diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 942c2eb72..75c5e171d 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -129,7 +129,7 @@ proc mget*(t: StringTableRef, key: string): var string {.deprecated.} = ## ``KeyError`` exception is raised. Use ```[]``` instead. get(t, key) -proc getOrDefault*(t: StringTableRef; key: string, default: string = nil): string = +proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string = var index = rawGet(t, key) if index >= 0: result = t.data[index].val else: result = default From 864467ade3e82c492645c3f3709613336365ba1c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 19 Feb 2018 12:14:45 +0000 Subject: [PATCH 7/9] Merges #7226 manually. --- lib/pure/httpclient.nim | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 4c40a81fe..f81e80766 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -150,6 +150,31 @@ proc code*(response: Response | AsyncResponse): HttpCode ## corresponding ``HttpCode``. return response.status[0 .. 2].parseInt.HttpCode +proc contentType*(response: Response | AsyncResponse): string = + ## Retrieves the specified response's content type. + ## + ## This is effectively the value of the "Content-Type" header. + response.headers.getOrDefault("content-type") + +proc contentLength*(response: Response | AsyncResponse): int = + ## Retrieves the specified response's content length. + ## + ## This is effectively the value of the "Content-Length" header. + ## + ## A ``ValueError`` exception will be raised if the value is not an integer. + var contentLengthHeader = response.headers.getOrDefault("Content-Length") + return contentLengthHeader.parseInt() + +proc lastModified*(response: Response | AsyncResponse): DateTime = + ## Retrieves the specified response's last modified time. + ## + ## This is effectively the value of the "Last-Modified" header. + ## + ## Raises a ``ValueError`` if the parsing fails or the value is not a correctly + ## formatted time. + var lastModifiedHeader = response.headers.getOrDefault("last-modified") + result = parse(lastModifiedHeader, "dd, dd MMM yyyy HH:mm:ss Z") + proc body*(response: Response): string = ## Retrieves the specified response's body. ## From b30b9207510a62b09a8e77706219a829a73c1d78 Mon Sep 17 00:00:00 2001 From: data-man Date: Mon, 19 Feb 2018 16:57:17 +0300 Subject: [PATCH 8/9] Fix date parsing for a bad inputs --- lib/pure/times.nim | 200 ++++++++++++++++++++++++++++------------ tests/stdlib/ttimes.nim | 49 ++++++++++ 2 files changed, 189 insertions(+), 60 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 50bf8f7f2..b571cd51c 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -936,24 +936,36 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = var sv: int case token of "d": - var pd = parseInt(value[j..j+1], sv) - dt.monthday = sv - j += pd - of "dd": - dt.monthday = value[j..j+1].parseInt() - j += 2 - of "ddd": - case value[j..j+2].toLowerAscii() - of "sun": dt.weekday = dSun - of "mon": dt.weekday = dMon - of "tue": dt.weekday = dTue - of "wed": dt.weekday = dWed - of "thu": dt.weekday = dThu - of "fri": dt.weekday = dFri - of "sat": dt.weekday = dSat + if value.len >= j+1: + var pd = parseInt(value[j..j+1], sv) + dt.monthday = sv + j += pd else: raise newException(ValueError, + "Couldn't parse day of month (d), got: " & value) + of "dd": + if value.len >= j+1: + dt.monthday = value[j..j+1].parseInt() + else: + raise newException(ValueError, + "Couldn't parse day of month (dd), got: " & value) + j += 2 + of "ddd": + if value.len >= j+2: + case value[j..j+2].toLowerAscii() + of "sun": dt.weekday = dSun + of "mon": dt.weekday = dMon + of "tue": dt.weekday = dTue + of "wed": dt.weekday = dWed + of "thu": dt.weekday = dThu + of "fri": dt.weekday = dFri + of "sat": dt.weekday = dSat + else: + raise newException(ValueError, "Couldn't parse day of week (ddd), got: " & value[j..j+2]) + else: + raise newException(ValueError, + "Couldn't parse day of week (ddd), got: " & value) j += 3 of "dddd": if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0: @@ -981,41 +993,69 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = raise newException(ValueError, "Couldn't parse day of week (dddd), got: " & value) of "h", "H": - var pd = parseInt(value[j..j+1], sv) - dt.hour = sv - j += pd + if value.len >= j+1: + var pd = parseInt(value[j..j+1], sv) + dt.hour = sv + j += pd + else: + raise newException(ValueError, + "Couldn't parse hour (h), got: " & value) of "hh", "HH": - dt.hour = value[j..j+1].parseInt() + if value.len >= j+1: + dt.hour = value[j..j+1].parseInt() + else: + raise newException(ValueError, + "Couldn't parse hour (hh), got: " & value) j += 2 of "m": - var pd = parseInt(value[j..j+1], sv) - dt.minute = sv - j += pd + if value.len >= j+1: + var pd = parseInt(value[j..j+1], sv) + dt.minute = sv + j += pd + else: + raise newException(ValueError, + "Couldn't parse minute (m), got: " & value) of "mm": - dt.minute = value[j..j+1].parseInt() + if value.len >= j+1: + dt.minute = value[j..j+1].parseInt() + else: + raise newException(ValueError, + "Couldn't parse minute (mm), got: " & value) j += 2 of "M": - var pd = parseInt(value[j..j+1], sv) - dt.month = sv.Month - j += pd + if value.len >= j+1: + var pd = parseInt(value[j..j+1], sv) + dt.month = sv.Month + j += pd + else: + raise newException(ValueError, + "Couldn't parse month (M), got: " & value) of "MM": - var month = value[j..j+1].parseInt() - j += 2 - dt.month = month.Month + if value.len >= j+1: + var month = value[j..j+1].parseInt() + j += 2 + dt.month = month.Month + else: + raise newException(ValueError, + "Couldn't parse month (MM), got: " & value) of "MMM": - case value[j..j+2].toLowerAscii(): - of "jan": dt.month = mJan - of "feb": dt.month = mFeb - of "mar": dt.month = mMar - of "apr": dt.month = mApr - of "may": dt.month = mMay - of "jun": dt.month = mJun - of "jul": dt.month = mJul - of "aug": dt.month = mAug - of "sep": dt.month = mSep - of "oct": dt.month = mOct - of "nov": dt.month = mNov - of "dec": dt.month = mDec + if value.len >= j+2: + case value[j..j+2].toLowerAscii(): + of "jan": dt.month = mJan + of "feb": dt.month = mFeb + of "mar": dt.month = mMar + of "apr": dt.month = mApr + of "may": dt.month = mMay + of "jun": dt.month = mJun + of "jul": dt.month = mJul + of "aug": dt.month = mAug + of "sep": dt.month = mSep + of "oct": dt.month = mOct + of "nov": dt.month = mNov + of "dec": dt.month = mDec + else: + raise newException(ValueError, + "Couldn't parse month (MMM), got: " & value) else: raise newException(ValueError, "Couldn't parse month (MMM), got: " & value) @@ -1061,35 +1101,63 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = raise newException(ValueError, "Couldn't parse month (MMMM), got: " & value) of "s": - var pd = parseInt(value[j..j+1], sv) - dt.second = sv - j += pd + if value.len >= j+1: + var pd = parseInt(value[j..j+1], sv) + dt.second = sv + j += pd + else: + raise newException(ValueError, + "Couldn't parse second (s), got: " & value) of "ss": - dt.second = value[j..j+1].parseInt() + if value.len >= j+1: + dt.second = value[j..j+1].parseInt() + else: + raise newException(ValueError, + "Couldn't parse second (ss), got: " & value) j += 2 of "t": if value[j] == 'P' and dt.hour > 0 and dt.hour < 12: dt.hour += 12 j += 1 of "tt": - if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12: - dt.hour += 12 + if value.len >= j+1: + if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12: + dt.hour += 12 + else: + raise newException(ValueError, + "Couldn't parse hour (tt), got: " & value) j += 2 of "yy": # Assumes current century - var year = value[j..j+1].parseInt() - var thisCen = now().year div 100 - dt.year = thisCen*100 + year + if value.len >= j+1: + var year = value[j..j+1].parseInt() + var thisCen = now().year div 100 + dt.year = thisCen*100 + year + else: + raise newException(ValueError, + "Couldn't parse year (yy), got: " & value) j += 2 of "yyyy": - dt.year = value[j..j+3].parseInt() + if value.len >= j+3: + dt.year = value[j..j+3].parseInt() + else: + raise newException(ValueError, + "Couldn't parse year (yyyy), got: " & value) j += 4 of "z": dt.isDst = false if value[j] == '+': - dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour + if value.len >= j+1: + dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour + else: + raise newException(ValueError, + "Couldn't parse timezone offset (z), got: " & value) elif value[j] == '-': - dt.utcOffset = parseInt($value[j+1]) * secondsInHour + if value.len >= j+1: + dt.utcOffset = parseInt($value[j+1]) * secondsInHour + else: + raise newException(ValueError, + "Couldn't parse timezone offset (z), got: " & value) elif value[j] == 'Z': dt.utcOffset = 0 j += 1 @@ -1101,9 +1169,17 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = of "zz": dt.isDst = false if value[j] == '+': - dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour + if value.len >= j+2: + dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour + else: + raise newException(ValueError, + "Couldn't parse timezone offset (zz), got: " & value) elif value[j] == '-': - dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour + if value.len >= j+2: + dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour + else: + raise newException(ValueError, + "Couldn't parse timezone offset (zz), got: " & value) elif value[j] == 'Z': dt.utcOffset = 0 j += 1 @@ -1124,10 +1200,14 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = else: raise newException(ValueError, "Couldn't parse timezone offset (zzz), got: " & value[j]) - dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour - j += 4 - dt.utcOffset += factor * value[j..j+1].parseInt() * 60 - j += 2 + if value.len >= j+5: + dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour + j += 4 + dt.utcOffset += factor * value[j..j+1].parseInt() * 60 + j += 2 + else: + raise newException(ValueError, + "Couldn't parse timezone offset (zzz), got: " & value) else: # Ignore the token and move forward in the value string by the same length j += token.len diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index 1f8ae6a22..ae056a79f 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -131,6 +131,10 @@ template parseTest(s, f, sExpected: string, ydExpected: int) = echo parsed.yearday, " exp: ", ydExpected check(parsed.yearday == ydExpected) +template parseTestExcp(s, f: string) = + expect ValueError: + let parsed = s.parse(f) + template parseTestTimeOnly(s, f, sExpected: string) = check sExpected in $s.parse(f, utc()) @@ -281,6 +285,51 @@ suite "ttimes": test "parseTest": runTimezoneTests() + test "incorrect inputs: empty string": + parseTestExcp("", "yyyy-MM-dd") + + test "incorrect inputs: year": + parseTestExcp("20-02-19", "yyyy-MM-dd") + + test "incorrect inputs: month number": + parseTestExcp("2018-2-19", "yyyy-MM-dd") + + test "incorrect inputs: month name": + parseTestExcp("2018-Fe", "yyyy-MMM-dd") + + test "incorrect inputs: day": + parseTestExcp("2018-02-1", "yyyy-MM-dd") + + test "incorrect inputs: day of week": + parseTestExcp("2018-Feb-Mo", "yyyy-MMM-ddd") + + test "incorrect inputs: hour": + parseTestExcp("2018-02-19 1:30", "yyyy-MM-dd hh:mm") + + test "incorrect inputs: minute": + parseTestExcp("2018-02-19 16:3", "yyyy-MM-dd hh:mm") + + test "incorrect inputs: second": + parseTestExcp("2018-02-19 16:30:0", "yyyy-MM-dd hh:mm:ss") + + test "incorrect inputs: timezone (z)": + parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss z") + + test "incorrect inputs: timezone (zz) 1": + parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zz") + + test "incorrect inputs: timezone (zz) 2": + parseTestExcp("2018-02-19 16:30:00 +1", "yyyy-MM-dd hh:mm:ss zz") + + test "incorrect inputs: timezone (zzz) 1": + parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zzz") + + test "incorrect inputs: timezone (zzz) 2": + parseTestExcp("2018-02-19 16:30:00 +01:", "yyyy-MM-dd hh:mm:ss zzz") + + test "incorrect inputs: timezone (zzz) 3": + parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz") + test "dynamic timezone": proc staticOffset(offset: int): Timezone = proc zoneInfoFromTz(adjTime: Time): ZonedTime = From ceebfb3e89e2417129fd9e817885c2d3b23a401f Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 19 Feb 2018 20:00:14 +0000 Subject: [PATCH 9/9] Remove repetitive index checks in b30b9207510a62b. --- lib/pure/times.nim | 206 +++++++++++++++------------------------------ 1 file changed, 67 insertions(+), 139 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b571cd51c..6c1e1fe87 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -933,39 +933,35 @@ proc `$`*(time: Time): string {.tags: [], raises: [], benign.} = proc parseToken(dt: var DateTime; token, value: string; j: var int) = ## Helper of the parse proc to parse individual tokens. + + # Overwrite system.`[]` to raise a ValueError on index out of bounds. + proc `[]`[T, U](s: string, x: HSlice[T, U]): string = + if x.a >= s.len or x.b >= s.len: + raise newException(ValueError, "Value is missing required tokens, got: " & + s) + return system.`[]`(s, x) + var sv: int case token of "d": - if value.len >= j+1: - var pd = parseInt(value[j..j+1], sv) - dt.monthday = sv - j += pd - else: - raise newException(ValueError, - "Couldn't parse day of month (d), got: " & value) + var pd = parseInt(value[j..j+1], sv) + dt.monthday = sv + j += pd of "dd": - if value.len >= j+1: - dt.monthday = value[j..j+1].parseInt() - else: - raise newException(ValueError, - "Couldn't parse day of month (dd), got: " & value) + dt.monthday = value[j..j+1].parseInt() j += 2 of "ddd": - if value.len >= j+2: - case value[j..j+2].toLowerAscii() - of "sun": dt.weekday = dSun - of "mon": dt.weekday = dMon - of "tue": dt.weekday = dTue - of "wed": dt.weekday = dWed - of "thu": dt.weekday = dThu - of "fri": dt.weekday = dFri - of "sat": dt.weekday = dSat - else: - raise newException(ValueError, - "Couldn't parse day of week (ddd), got: " & value[j..j+2]) + case value[j..j+2].toLowerAscii() + of "sun": dt.weekday = dSun + of "mon": dt.weekday = dMon + of "tue": dt.weekday = dTue + of "wed": dt.weekday = dWed + of "thu": dt.weekday = dThu + of "fri": dt.weekday = dFri + of "sat": dt.weekday = dSat else: - raise newException(ValueError, - "Couldn't parse day of week (ddd), got: " & value) + raise newException(ValueError, + "Couldn't parse day of week (ddd), got: " & value[j..j+2]) j += 3 of "dddd": if value.len >= j+6 and value[j..j+5].cmpIgnoreCase("sunday") == 0: @@ -993,69 +989,41 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = raise newException(ValueError, "Couldn't parse day of week (dddd), got: " & value) of "h", "H": - if value.len >= j+1: - var pd = parseInt(value[j..j+1], sv) - dt.hour = sv - j += pd - else: - raise newException(ValueError, - "Couldn't parse hour (h), got: " & value) + var pd = parseInt(value[j..j+1], sv) + dt.hour = sv + j += pd of "hh", "HH": - if value.len >= j+1: - dt.hour = value[j..j+1].parseInt() - else: - raise newException(ValueError, - "Couldn't parse hour (hh), got: " & value) + dt.hour = value[j..j+1].parseInt() j += 2 of "m": - if value.len >= j+1: - var pd = parseInt(value[j..j+1], sv) - dt.minute = sv - j += pd - else: - raise newException(ValueError, - "Couldn't parse minute (m), got: " & value) + var pd = parseInt(value[j..j+1], sv) + dt.minute = sv + j += pd of "mm": - if value.len >= j+1: - dt.minute = value[j..j+1].parseInt() - else: - raise newException(ValueError, - "Couldn't parse minute (mm), got: " & value) + dt.minute = value[j..j+1].parseInt() j += 2 of "M": - if value.len >= j+1: - var pd = parseInt(value[j..j+1], sv) - dt.month = sv.Month - j += pd - else: - raise newException(ValueError, - "Couldn't parse month (M), got: " & value) + var pd = parseInt(value[j..j+1], sv) + dt.month = sv.Month + j += pd of "MM": - if value.len >= j+1: - var month = value[j..j+1].parseInt() - j += 2 - dt.month = month.Month - else: - raise newException(ValueError, - "Couldn't parse month (MM), got: " & value) + var month = value[j..j+1].parseInt() + j += 2 + dt.month = month.Month of "MMM": - if value.len >= j+2: - case value[j..j+2].toLowerAscii(): - of "jan": dt.month = mJan - of "feb": dt.month = mFeb - of "mar": dt.month = mMar - of "apr": dt.month = mApr - of "may": dt.month = mMay - of "jun": dt.month = mJun - of "jul": dt.month = mJul - of "aug": dt.month = mAug - of "sep": dt.month = mSep - of "oct": dt.month = mOct - of "nov": dt.month = mNov - of "dec": dt.month = mDec - else: - raise newException(ValueError, - "Couldn't parse month (MMM), got: " & value) + case value[j..j+2].toLowerAscii(): + of "jan": dt.month = mJan + of "feb": dt.month = mFeb + of "mar": dt.month = mMar + of "apr": dt.month = mApr + of "may": dt.month = mMay + of "jun": dt.month = mJun + of "jul": dt.month = mJul + of "aug": dt.month = mAug + of "sep": dt.month = mSep + of "oct": dt.month = mOct + of "nov": dt.month = mNov + of "dec": dt.month = mDec else: raise newException(ValueError, "Couldn't parse month (MMM), got: " & value) @@ -1101,63 +1069,35 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = raise newException(ValueError, "Couldn't parse month (MMMM), got: " & value) of "s": - if value.len >= j+1: - var pd = parseInt(value[j..j+1], sv) - dt.second = sv - j += pd - else: - raise newException(ValueError, - "Couldn't parse second (s), got: " & value) + var pd = parseInt(value[j..j+1], sv) + dt.second = sv + j += pd of "ss": - if value.len >= j+1: - dt.second = value[j..j+1].parseInt() - else: - raise newException(ValueError, - "Couldn't parse second (ss), got: " & value) + dt.second = value[j..j+1].parseInt() j += 2 of "t": if value[j] == 'P' and dt.hour > 0 and dt.hour < 12: dt.hour += 12 j += 1 of "tt": - if value.len >= j+1: - if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12: - dt.hour += 12 - else: - raise newException(ValueError, - "Couldn't parse hour (tt), got: " & value) + if value[j..j+1] == "PM" and dt.hour > 0 and dt.hour < 12: + dt.hour += 12 j += 2 of "yy": # Assumes current century - if value.len >= j+1: - var year = value[j..j+1].parseInt() - var thisCen = now().year div 100 - dt.year = thisCen*100 + year - else: - raise newException(ValueError, - "Couldn't parse year (yy), got: " & value) + var year = value[j..j+1].parseInt() + var thisCen = now().year div 100 + dt.year = thisCen*100 + year j += 2 of "yyyy": - if value.len >= j+3: - dt.year = value[j..j+3].parseInt() - else: - raise newException(ValueError, - "Couldn't parse year (yyyy), got: " & value) + dt.year = value[j..j+3].parseInt() j += 4 of "z": dt.isDst = false if value[j] == '+': - if value.len >= j+1: - dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour - else: - raise newException(ValueError, - "Couldn't parse timezone offset (z), got: " & value) + dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour elif value[j] == '-': - if value.len >= j+1: - dt.utcOffset = parseInt($value[j+1]) * secondsInHour - else: - raise newException(ValueError, - "Couldn't parse timezone offset (z), got: " & value) + dt.utcOffset = parseInt($value[j+1]) * secondsInHour elif value[j] == 'Z': dt.utcOffset = 0 j += 1 @@ -1169,17 +1109,9 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = of "zz": dt.isDst = false if value[j] == '+': - if value.len >= j+2: - dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour - else: - raise newException(ValueError, - "Couldn't parse timezone offset (zz), got: " & value) + dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour elif value[j] == '-': - if value.len >= j+2: - dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour - else: - raise newException(ValueError, - "Couldn't parse timezone offset (zz), got: " & value) + dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour elif value[j] == 'Z': dt.utcOffset = 0 j += 1 @@ -1200,14 +1132,10 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) = else: raise newException(ValueError, "Couldn't parse timezone offset (zzz), got: " & value[j]) - if value.len >= j+5: - dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour - j += 4 - dt.utcOffset += factor * value[j..j+1].parseInt() * 60 - j += 2 - else: - raise newException(ValueError, - "Couldn't parse timezone offset (zzz), got: " & value) + dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour + j += 4 + dt.utcOffset += factor * value[j..j+1].parseInt() * 60 + j += 2 else: # Ignore the token and move forward in the value string by the same length j += token.len