Stefan Fuhrmann wrote on Sat, Nov 05, 2016 at 22:01:03 +0100:
> On 05.11.2016 17:41, Daniel Shahaf wrote:
> >Stefan Fuhrmann wrote on Sat, Nov 05, 2016 at 13:52:49 +0100:
> >>On 31.10.2016 01:45, Daniel Shahaf wrote:
> >>>[email protected] wrote on Sun, Oct 30, 2016 at 23:43:07 -0000:
> >>>>+ dirent: ( rel-path:string kind:node-kind ? ( size:number
> >>>>+ has-props:bool created-rev:number
> >>>>+ [ created-date:string ] [ last-author:string ] ) )
> >>>>+ | done
> >>>
> >>>Why should size,has-props,created-rev be all present or all absent?
> >>>Wouldn't it be better to let each element (other than the first two) be
> >>>optional, i.e.,
> >>>
> >>> dirent: ( rel-path:string kind:node-kind
> >>> ? [ size:number ]
> >>> [ has-props:bool ]
> >>> [ created-rev:number ]
> >>> [ created-date:string ]
> >>> [ last-author:string ] )
> >>>
> >>>? This decouples the protocol from the backend (specifically, from what
> >>>bits the backend has cheaply available).
> >>
> >>Yes, that would be better.
> >>
> >>It would require to either make has_props a tristate
> >>or extending the protocol to allow for optional bools.
> >>
> >>I think we should do the latter because regardless of
> >>whether we have them at the protocol level or not, we
> >>must specify a value in the output data struct at the
> >>receiver side. So, we might as well define it to FALSE
> >>for n/a data.
> >
> >I don't quite follow, sorry. Let me just clarify my idea:
> >
> >We'll use the spec I gave above, with «[ foo:bool ]», which is an
> >optional bool. At the protocol level, that suffices; it is equivalent
> >to a non-optional «foo:tristate», but without needing to add
> >a "tristate" type to the wire protocol. At the API level, we'd provide
> >two parsing options: '3' to parse the optional bool into an
> >svn_tristate_t, and 'B' to parse the optional bool into an
> >svn_boolean_t, converting svn_tristate_unknown to FALSE. Using 'b' to
> >parse an optional error should be a fatal error (an SVN_ERR_ASSERT).
>
> Well, B does not write to a svn_boolean_t but to an uint64.
>
Well, okay. My point was just that the parser function could let its
caller decide how an absent optional bool would be represented, by
specifying one format character for "wire boolean as svn_boolean_t
('absent' maps to FALSE)" and another format character for "wire boolean
as svn_tristate_t".
> >Makes sense? Could you clarify your idea as well?
>
> I was more looking at the parser / writer code and its format
> specification options.
>
> Not allowing b to be part of e.g. an optional struct makes
> the parser API slightly less easy to use. That does not take
> away from the fact that optional bools are effectively tri-
> states and the API user needs to decide how to treat missing
> data by specifying the suitable data format string.
>
The idea behind forbidding 'b' to be optional in the parser function was
to force callers to choose a behaviour for when the boolean was absent.
(As PEP 20 says — "Explicit is better than implicit".)
> >>And while we are at it, we should make vwrite_tuple()
> >>actually support optional tristates and numbers.
> >
> >What do you mean by "optional tristate"? It sounds like you're talking
> >about a «[ foo:tristate ]», which doesn't appear in the current protocol.
> >If the question is, how would one call vwrite_tuple() to generate
> >a tuple specified as «[ foo:bool ]», then I think there are three ways:
> >
> >* vwrite_tuple("()")
> >* vwrite_tuple("(b)", (svn_boolean_t)foo)
> >* vwrite_tuple("3", (svn_tristate_t)bar)
>
> The last one is currently not supported. That is not symmetric
> to the receiver side where "3" is a support format spec.
>
Right. I see now that the writer function already supports "?", so
adding parentheses to the third case would make sense:
vwrite_tuple("(?X)", (svn_tristate_t)baz)
would generate either «( ) » or «( false ) » or «( true ) ».
> Worse, trying to write an optional number using a "(?n)" format
> causes a malfunction today.
>
That's just a bug. The attached patch should fix it (untested).
> Where they make sense, I'd like to support all format specs and
> combinations in the parser and write - even if we might not use
> them right now.
Personally, I wouldn't worry about this too much; we can implement these
things as and when we need them with little effort.
> The only thing we can't support meaningfully
> on the sender side is a "?b" because we don't have a n/a value
> to check for.
So we'd need some new format code which means "take an svn_tristate_t
argument and render it as a wire bool"; something like the "(?X)"
hereinabove.
The catch is that the 'X' format character should be prohibited in the
*non*-optional part of a tuple, since it's supposed to be marshalled as
a bool, and svn_tristate_unknown is then unrepresentable.
Proof of concept attached. (I used 'X' as a placeholder; we can pick
some more suitable format character)
Cheers,
Daniel
Index: subversion/libsvn_ra_svn/marshal.c
===================================================================
--- subversion/libsvn_ra_svn/marshal.c (revision 1767714)
+++ subversion/libsvn_ra_svn/marshal.c (working copy)
@@ -913,6 +913,15 @@ vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn,
}
static svn_error_t *
+vwrite_tuple_number_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ apr_uint64_t number = va_arg(*ap, apr_uint64_t);
+ if (number != SVN_RA_SVN_UNSPECIFIED_NUMBER)
+ SVN_ERR(svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t)));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
{
return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
@@ -1157,7 +1166,8 @@ static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t
SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
: vwrite_tuple_revision(conn, pool, ap));
else if (*fmt == 'n' && !opt)
- SVN_ERR(vwrite_tuple_number(conn, pool, ap));
+ SVN_ERR(opt ? vwrite_tuple_number_opt(conn, pool, ap)
+ : vwrite_tuple_number(conn, pool, ap));
else if (*fmt == 'b' && !opt)
SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
else if (*fmt == '!' && !*(fmt + 1))
Index: subversion/libsvn_ra_svn/marshal.c
===================================================================
--- subversion/libsvn_ra_svn/marshal.c (revision 1767714)
+++ subversion/libsvn_ra_svn/marshal.c (working copy)
@@ -925,6 +925,29 @@ vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_
}
static svn_error_t *
+vwrite_tuple_boolean_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
+{
+ switch (va_arg(*ap, svn_tristate_t))
+ {
+ case svn_tristate_unknown:
+ break;
+
+ case svn_tristate_false:
+ SVN_ERR(svn_ra_svn__write_boolean(conn, pool, FALSE));
+ break;
+
+ case svn_tristate_true:
+ SVN_ERR(svn_ra_svn__write_boolean(conn, pool, TRUE));
+ break;
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
write_tuple_cstring(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *cstr)
@@ -1160,6 +1183,8 @@ static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t
SVN_ERR(vwrite_tuple_number(conn, pool, ap));
else if (*fmt == 'b' && !opt)
SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
+ else if (*fmt == 'X' && opt)
+ SVN_ERR(vwrite_tuple_boolean_opt(conn, pool, ap));
else if (*fmt == '!' && !*(fmt + 1))
return SVN_NO_ERROR;
else