Enhanced C#
Loyc library documentation
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events Pages
Public fields | Properties | Public Member Functions | List of all members
Loyc.SyncLib.ISyncManager Interface Reference

This is the central interface of Loyc.SyncLib. To learn more, please visit the web site: http://synclib.loyc.net/ More...


Source file:

Remarks

This is the central interface of Loyc.SyncLib. To learn more, please visit the web site: http://synclib.loyc.net/

Note: it is recommended (but not required) that Sync() methods in reader implementations support the following implicit type conversions:

Public fields

bool Begun
 Attempts to begin reading or writing a sub-object. This is a low-level method; end-users normally should NOT call it! Be sure to fully read the documentation before use. More...
 
bool int Length
 

Properties

SyncMode Mode [get]
 Indicates what kind of synchronizer this is: one that saves data, one that loads data, or one that saves a schema. More...
 
bool IsReading [get]
 Returns true if the current Mode is SyncMode.Reading, SyncMode.Schema or SyncMode.Merge. If your synchronizer method behaves differently when it is loading than when it is saving, it's often more appropriate to get this property rather than testing whether Mode == SyncMode.Loading, because if the current mode is Schema or Merge, your synchronizer method should do most of its "loading" behavior. More...
 
bool IsWriting [get]
 Returns true if the current Mode is SyncMode.Writing, SyncMode.Query or SyncMode.Merge. If your synchronizer method behaves differently when it is loading than when it is saving, you should almost always get this property rather than testing whether Mode == SyncMode.Saving, because if the current mode is Query or Merge, your synchronizer method should usually do the same thing it does when saving. More...
 
bool SupportsReordering [get]
 Indicates that the serialized format has some kind of schema that enables fields to be read in a different order than they were written (e.g. JSON, Protobuf). If this field is false, fields must be read in the same order they were written, and omitting fields is not allowed (e.g. you cannot skip over a null field without saving it, nor skip over a field and then read it later). More...
 
bool SupportsNextField [get]
 Indicates that this implementation of ISyncManager supports the NextField property. (This property must be false when IsReading is false.) More...
 
bool SupportsDeduplication [get]
 Returns true if the ISyncManager supports deduplication of objects and cyclic object graphs. Note: all standard implementations of this interface do support deduplication and cyclic object graphs. More...
 
bool IsInsideList [get]
 Indicates that the properties of the current sub-object do not have names because the basic ObjectMode is either ObjectMode.Tuple or ObjectMode.List. In this case, SupportsReordering is irrelevant, since fields do not have names or ID numbers. More...
 
bool NeedsIntegerIds [get]
 Indicates that the serialized format uses field ID numbers rather than field names (e.g. Protocol Buffers). When using strings or global symbols, the ID is indicated implcitly via field order: the first field read or written has ID=1, the second has ID=2, etc. If you need to customize the field numbers, you can do so by creating a private Loyc.SymbolPool and creating symbols with custom ID numbers. More...
 
bool ReachedEndOfList [get]
 This is a low-level property that most code should NOT call directly. If a list is being scanned and the current object can detect the end of the list (e.g. the mode is SyncMode.Loading and BeginSubObject returned a length of int.MaxValue), this property returns a boolean value: true at the end of the list and false otherwise. In all other cases, null is returned. More...
 
int MinimumListLength [get]
 This is a low-level property; end-users normally should NOT call it! If a variable-length list is being scanned in Loading mode, this property returns either the list length (if known) or the minimum list length (if the total length is not known before reading the list). This property is null in Saving mode, or when a variable-length list is not being scanned. More...
 
int Depth [get]
 Returns the number of objects for which BeginSubObject opened an object, list or tuple that was not closed EndSubObject. This property is zero if BeginSubObject was never called to start a subobject. More...
 
bool IsPlainText [get]
 true if the current format is a plain-text rather than binary format. This property may be used, for example, to decide whether to output a human- friendly encoding of certain data types such as DateTime. More...
 
ISyncOptions Options [get]
 Gets the current Options object for this manager (its type is specific to the manager). Note: this is meant for reading options. The manager might not be designed for options to be changed after a sync has started. More...
 
FieldId NextField [get]
 If SupportsNextField is true, the end of the current object has not been reached, and IsInsideList is false, this property returns the name or integer ID of the next field in the input stream. Otherwise, it returns FieldId.Missing. More...
 
object CurrentObject [set]
 Sets the "current object" reference. This property must be set when deserializing object graphs with cycles (see remarks). More...
 

Public Member Functions

SyncType GetFieldType (FieldId name, SyncType expectedType=SyncType.Unknown)
 Some serializers do not support this method (see remarks). If the method is supported, it determines whether a field with a specific name exists, and if so, what type it has. More...
 
string SyncTypeTag (string?tag)
 Reads or writes a "type tag" for the current object. This is a low-level method; end-users normally should NOT call it! This method can only be called once after BeginSubObject returns true, and can only be called before synchronizing the first subfield (i.e. before calling any of the Sync() methods). More...
 
bool Sync (FieldId name, bool savable)
 Reads or writes a value of a field on the current object. More...
 
sbyte Sync (FieldId name, sbyte savable)
 Reads or writes a value of a field on the current object. More...
 
byte Sync (FieldId name, byte savable)
 Reads or writes a value of a field on the current object. More...
 
short Sync (FieldId name, short savable)
 Reads or writes a value of a field on the current object. More...
 
ushort Sync (FieldId name, ushort savable)
 Reads or writes a value of a field on the current object. More...
 
int Sync (FieldId name, int savable)
 Reads or writes a value of a field on the current object. More...
 
uint Sync (FieldId name, uint savable)
 Reads or writes a value of a field on the current object. More...
 
long Sync (FieldId name, long savable)
 Reads or writes a value of a field on the current object. More...
 
ulong Sync (FieldId name, ulong savable)
 Reads or writes a value of a field on the current object. More...
 
float Sync (FieldId name, float savable)
 Reads or writes a value of a field on the current object. More...
 
double Sync (FieldId name, double savable)
 Reads or writes a value of a field on the current object. More...
 
decimal Sync (FieldId name, decimal savable)
 Reads or writes a value of a field on the current object. More...
 
BigInteger Sync (FieldId name, BigInteger savable)
 Reads or writes a value of a field on the current object. More...
 
char Sync (FieldId name, char savable)
 Reads or writes a value of a field on the current object. More...
 
string Sync (FieldId name, string?savablem, ObjectMode mode=ObjectMode.Normal)
 Reads or writes a value of a field on the current object. More...
 
int Sync (FieldId name, int savable, int bits, bool signed=true)
 Reads or writes a value of an integer bitfield on the current object. More...
 
long Sync (FieldId name, long savable, int bits, bool signed=true)
 Reads or writes a value of an integer bitfield on the current object. More...
 
BigInteger Sync (FieldId name, BigInteger savable, int bits, bool signed=true)
 Reads or writes a value of an integer bitfield on the current object. More...
 
bool Sync (FieldId name, bool?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
sbyte Sync (FieldId name, sbyte?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
byte Sync (FieldId name, byte?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
short Sync (FieldId name, short?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
ushort Sync (FieldId name, ushort?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
int Sync (FieldId name, int?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
uint Sync (FieldId name, uint?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
long Sync (FieldId name, long?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
ulong Sync (FieldId name, ulong?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
float Sync (FieldId name, float?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
double Sync (FieldId name, double?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
decimal Sync (FieldId name, decimal?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
BigInteger Sync (FieldId name, BigInteger?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
char Sync (FieldId name, char?savable)
 Reads or writes a value of a nullable field on the current object. More...
 
List SyncListBoolImpl< Scanner, List, ListBuilder > (FieldId name, Scanner scanner, List?saving, ListBuilder builder, ObjectMode mode, int tupleOrListLength=-1)
 This method is used by SyncList() extension methods to read or write an array. Users don't need to call it. More...
 
List SyncListCharImpl< Scanner, List, ListBuilder > (FieldId name, Scanner scanner, List?saving, ListBuilder builder, ObjectMode mode, int tupleOrListLength=-1)
 This method is used by SyncList() extension methods to read or write an array. Users don't need to call it. More...
 
List SyncListByteImpl< Scanner, List, ListBuilder > (FieldId name, Scanner scanner, List?saving, ListBuilder builder, ObjectMode mode, int tupleOrListLength=-1)
 This method is used by SyncList() extension methods to read or write an array. Users don't need to call it. More...
 
bool int object Object BeginSubObject (FieldId name, object?childKey, ObjectMode mode, int listLength=-1)
 
void EndSubObject ()
 If you called BeginSubObject and it returned true, you must call this method when you're done loading/saving the sub-object. Do not call this method otherwise. More...
 

Member Function Documentation

void Loyc.SyncLib.ISyncManager.EndSubObject ( )

If you called BeginSubObject and it returned true, you must call this method when you're done loading/saving the sub-object. Do not call this method otherwise.

SyncType Loyc.SyncLib.ISyncManager.GetFieldType ( FieldId  name,
SyncType  expectedType = SyncType.Unknown 
)

Some serializers do not support this method (see remarks). If the method is supported, it determines whether a field with a specific name exists, and if so, what type it has.

Parameters
nameName to search for in the current stream. If this parameter is FieldId.Missing it is interpreted as a request to get the type of the next field or the next list item, which is only supported if (1) SupportsNextField or (2) SupportsReordering and IsInsideList. If NeedsIntegerIds is true then the FieldId needs an id number to search for.
expectedTypeThe type that the caller expects to encounter, or SyncType.Unknown if the caller has no preference.
Returns

Returns SyncType.Missing if the field does not exist, or if the field exists but the field's type is not implicitly convertible to the expected type.

Returns SyncType.Unknown if it cannot be determined whether the field exists, or if this ISyncManager is not a reader (Mode != SyncMode.Loading), or if IsInsideList is true.

Otherwise, an appropriate value of SyncType is returned according to the data in the data stream.

Generally, this method can provide useful information only if IsReading, when (1) SupportsReordering is true or (2) name is FieldId.Missing and SupportsNextField. If these conditions are not met, the method normally returns SyncType.Unknown.

The SyncType enumeration has a collection of common types that are supported by most data formats. However, some formats may not support all types, or may store data in a different form than you might reasonably expect. For example, when a byte array is stored in JSON, it is stored as a string by default. When reading a byte array from JSON, GetFieldType will report that the type is SyncType.String because even though the reader is capable of decoding the string as a byte array, it cannot know that the string represents a byte array. For this reason, the expectedType parameter exists as a filtering technique. You can set this parameter to SyncType.ByteList to indicate that you expect to read a byte array. If the JSON data type is boolean, which cannot be interpreted as a byte array, GetFieldType() returns SyncType.Missing. But if the JSON data type is string, GetFieldType() returns SyncType.String. This indicates that the data stream contains a String that is potentially convertible to SyncType.ByteList, although the conversion is not guaranteed to work. If you then read this field by calling Sync(name, (byte[]) null) and it turns out that the string cannot be interpreted as a byte array, an exception will be thrown. After catching this exception, it may or may not be possible to continue reading from the stream, depending on whether the ISyncManager was designed to keep working after that kind of failure.

If a value is not implicitly convertible to the expectedType, GetFieldType should return SyncType.Missing even if the conversion is supported. For example, if the actual type is Char but a Byte was expected, the ISyncManager implementation may support this conversion by masking off the lowest 8 bits, but it should return SyncType.Missing because this kind of conversion loses information and may not be what the user intended. (It may seem like this is somewhat in contradiction with the previous paragraph, because JSON pretends String matches ByteList even though the conversion may fail. However, the fact that a ByteList is stored as a String is an implementation detail that your code should not explicitly deal with, so GetFieldType must report that the conversion is supported in order not to confuse code that is unaware of the implementation detail.)

It is recommended that implementations of ISyncManager support an implicit conversion from boolean to number, as well as conversions from "smaller" to "bigger" number types.

bool Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
bool  savable 
)

Reads or writes a value of a field on the current object.

sbyte Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
sbyte  savable 
)

Reads or writes a value of a field on the current object.

byte Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
byte  savable 
)

Reads or writes a value of a field on the current object.

short Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
short  savable 
)

Reads or writes a value of a field on the current object.

ushort Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
ushort  savable 
)

Reads or writes a value of a field on the current object.

int Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
int  savable 
)

Reads or writes a value of a field on the current object.

uint Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
uint  savable 
)

Reads or writes a value of a field on the current object.

long Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
long  savable 
)

Reads or writes a value of a field on the current object.

ulong Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
ulong  savable 
)

Reads or writes a value of a field on the current object.

float Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
float  savable 
)

Reads or writes a value of a field on the current object.

double Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
double  savable 
)

Reads or writes a value of a field on the current object.

decimal Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
decimal  savable 
)

Reads or writes a value of a field on the current object.

BigInteger Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
BigInteger  savable 
)

Reads or writes a value of a field on the current object.

char Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
char  savable 
)

Reads or writes a value of a field on the current object.

string Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
string?  savablem,
ObjectMode  mode = ObjectMode.Normal 
)

Reads or writes a value of a field on the current object.

Set mode to ObjectMode.Deduplicate to deduplicate the string, if supported by the current instance of ISyncManager.

int Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
int  savable,
int  bits,
bool  signed = true 
)

Reads or writes a value of an integer bitfield on the current object.

long Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
long  savable,
int  bits,
bool  signed = true 
)

Reads or writes a value of an integer bitfield on the current object.

BigInteger Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
BigInteger  savable,
int  bits,
bool  signed = true 
)

Reads or writes a value of an integer bitfield on the current object.

bool Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
bool?  savable 
)

Reads or writes a value of a nullable field on the current object.

sbyte Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
sbyte?  savable 
)

Reads or writes a value of a nullable field on the current object.

byte Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
byte?  savable 
)

Reads or writes a value of a nullable field on the current object.

short Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
short?  savable 
)

Reads or writes a value of a nullable field on the current object.

ushort Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
ushort?  savable 
)

Reads or writes a value of a nullable field on the current object.

int Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
int?  savable 
)

Reads or writes a value of a nullable field on the current object.

uint Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
uint?  savable 
)

Reads or writes a value of a nullable field on the current object.

long Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
long?  savable 
)

Reads or writes a value of a nullable field on the current object.

ulong Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
ulong?  savable 
)

Reads or writes a value of a nullable field on the current object.

float Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
float?  savable 
)

Reads or writes a value of a nullable field on the current object.

double Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
double?  savable 
)

Reads or writes a value of a nullable field on the current object.

decimal Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
decimal?  savable 
)

Reads or writes a value of a nullable field on the current object.

BigInteger Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
BigInteger?  savable 
)

Reads or writes a value of a nullable field on the current object.

char Loyc.SyncLib.ISyncManager.Sync ( FieldId  name,
char?  savable 
)

Reads or writes a value of a nullable field on the current object.

List Loyc.SyncLib.ISyncManager.SyncListBoolImpl< Scanner, List, ListBuilder > ( FieldId  name,
Scanner  scanner,
List saving,
ListBuilder  builder,
ObjectMode  mode,
int  tupleOrListLength = -1 
)

This method is used by SyncList() extension methods to read or write an array. Users don't need to call it.

Parameters
scannerA scanner for the list that the caller wants to save. This parameter is ignored when not saving.
savingThe object being saved. This parameter is used to produce an object ID for deduplication, and it can be null if the user actually provided a null reference, or if the mode includes ObjectMode.NotNull and not ObjectMode.Deduplicate.
builderA builder used to construct a new list when loading. This parameter is ignored when not loading.
tupleOrListLengthWhen mode includes ObjectMode.Tuple, this is a constant specifying the tuple length (which, if IsWriting, must match the list length). Otherwise, if IsWriting, this parameter must specify the number of items that the scanner will return (i.e. saving.Count). Otherwise, this parameter is ignored.
Returns
Returns default(TList) in Saving, Query and Schema modes. Otherwise, the data that was loaded via the builder is returned.

If you're trying to implement ISyncManager, please see the extra documentation above this method in ISyncManager.ecs in SyncLib's GitHub repo.

Type Constraints
Scanner :IScanner<bool> 
ListBuilder :IListBuilder 
ListBuilder :List 
ListBuilder :bool 
List Loyc.SyncLib.ISyncManager.SyncListByteImpl< Scanner, List, ListBuilder > ( FieldId  name,
Scanner  scanner,
List saving,
ListBuilder  builder,
ObjectMode  mode,
int  tupleOrListLength = -1 
)

This method is used by SyncList() extension methods to read or write an array. Users don't need to call it.

Parameters
scannerA scanner for the list that the caller wants to save. This parameter is ignored when not saving.
savingThe object being saved. This parameter is used to produce an object ID for deduplication, and it can be null if the user actually provided a null reference, or if the mode includes ObjectMode.NotNull and not ObjectMode.Deduplicate.
builderA builder used to construct a new list when loading. This parameter is ignored when not loading.
tupleOrListLengthWhen mode includes ObjectMode.Tuple, this is a constant specifying the tuple length (which, if IsWriting, must match the list length). Otherwise, if IsWriting, this parameter must specify the number of items that the scanner will return (i.e. saving.Count). Otherwise, this parameter is ignored.
Returns
Returns default(TList) in Saving, Query and Schema modes. Otherwise, the data that was loaded via the builder is returned.

If you're trying to implement ISyncManager, please see the extra documentation above this method in ISyncManager.ecs in SyncLib's GitHub repo.

Type Constraints
Scanner :IScanner<byte> 
ListBuilder :IListBuilder 
ListBuilder :List 
ListBuilder :byte 
List Loyc.SyncLib.ISyncManager.SyncListCharImpl< Scanner, List, ListBuilder > ( FieldId  name,
Scanner  scanner,
List saving,
ListBuilder  builder,
ObjectMode  mode,
int  tupleOrListLength = -1 
)

This method is used by SyncList() extension methods to read or write an array. Users don't need to call it.

Parameters
scannerA scanner for the list that the caller wants to save. This parameter is ignored when not saving.
savingThe object being saved. This parameter is used to produce an object ID for deduplication, and it can be null if the user actually provided a null reference, or if the mode includes ObjectMode.NotNull and not ObjectMode.Deduplicate.
builderA builder used to construct a new list when loading. This parameter is ignored when not loading.
tupleOrListLengthWhen mode includes ObjectMode.Tuple, this is a constant specifying the tuple length (which, if IsWriting, must match the list length). Otherwise, if IsWriting, this parameter must specify the number of items that the scanner will return (i.e. saving.Count). Otherwise, this parameter is ignored.
Returns
Returns default(TList) in Saving, Query and Schema modes. Otherwise, the data that was loaded via the builder is returned.

If you're trying to implement ISyncManager, please see the extra documentation above this method in ISyncManager.ecs in SyncLib's GitHub repo.

Type Constraints
Scanner :IScanner<char> 
ListBuilder :IListBuilder 
ListBuilder :List 
ListBuilder :char 
string Loyc.SyncLib.ISyncManager.SyncTypeTag ( string?  tag)

Reads or writes a "type tag" for the current object. This is a low-level method; end-users normally should NOT call it! This method can only be called once after BeginSubObject returns true, and can only be called before synchronizing the first subfield (i.e. before calling any of the Sync() methods).

Parameters
tagThe type tag to write. If IsWriting is false, this parameter is ignored.
Returns

When IsReading is true, the return value is the tag stored in the data stream, or null if there is no tag. When IsReading is false, the return value is tag.

If SupportsNextField is false, in order to read a data stream correctly, you must call this method if and only if this method was called when writing the stream.

No behavior has been defined for this method in SyncMode.Merge mode, when IsReading and IsWriting are both true.

Member Data Documentation

bool Loyc.SyncLib.ISyncManager.Begun

Attempts to begin reading or writing a sub-object. This is a low-level method; end-users normally should NOT call it! Be sure to fully read the documentation before use.

Parameters
nameThe name of the property being loaded or saved in the current object.
childKeyIf the current Mode is Saving or Query, this must be a reference that represents the object being saved, or null if the object is null. In Loading mode, ISyncManager ignores this parameter. If a value type is being read/written, you can avoid a memory allocation for boxing by setting this parameter to null (or better, to typeof(T), which also lets a schema saver identify the type), but only if you use a mode that includes ObjectMode.NotNull and not ObjectMode.Deduplicate.

In Schema mode there is no data, so this parameter instead identifies the schema of the sub-object (helper methods such as SyncManagerExt.Sync{SM, T}(SM, FieldId, T, SyncObjectFunc{SM, T}, ObjectMode) pass typeof(T)). A schema saver uses this key to give the sub-object's type a name, to avoid re-recording a schema it has already seen (by returning Begun = false), and to break cycles when the object graph's schema is recursive. If childKey is a non-null object that is not a Type, it is treated as a sample instance, whose type identifies the schema. If childKey is null in Schema mode, the sub-object is treated as an anonymous object, which works, but its schema is not deduplicated and cannot be recursive.

Parameters
modeSee ObjectMode for information about the possible modes. When ObjectMode.NotNull is present and ObjectMode.Deduplicate is absent, the value of childKey is ignored.
listLengthIf a variable-length list is being written (i.e. (mode & ObjectMode.List) != 0 && Mode is SyncMode.Saving or SyncMode.Query), this must specify the list length. Implementations of this interface that use delimiters (e.g. JSON) will ignore this parameter, but others will write the length to the output stream at the beginning of the list.
Returns

The boolean value is true if the request to read/write is approved. In this case you are expected to write the contents of the object and then call EndSubObject(). If the boolean is false, the request was declined and you MUST NOT write the fields of the sub-object, and you MUST NOT call EndSubObject().

The second return value, the Length, is relevant only when reading, and only when Begun is true. If Begun is false, you should always ignore the value. When reading a normal object, Length will be 1. When reading a tuple, Length will be equal to the listLength parameter that the caller provided. When reading a list, the return value is either (1) int.MaxValue, if the list length is unknown when the process of reading the list begins (e.g. JSON). (2) some other value indicating the list length that was read from the input stream (e.g. SyncBinary).

The third return value is a reference to a deduplicated object, a reference to childKey, or null, depending on the situation (see remarks).

When calling BeginList with a listLength parameter, there is a third return value, the list length, which is the number of elements you are expected to read or write.

Exceptions
ArgumentExceptionmode includes ObjectMode.List, listLength was negative, and Mode is SyncMode.Saving or SyncMode.Query. Implementations that don't require the list length in advance do not necessarily throw this exception.
FormatExceptionThe Mode is Reading or Merge and the input stream is invalid or does not contain an object or list by the specified name, so it is impossible to fulfill the request.

Typical implementations of ISyncManager are used via helper methods that call this method and write a single object. For example, SyncJson.Write and SyncManagerExt.Sync{SM, SyncField, T}(SM, FieldId, T?, SyncField) call both BeginSubObject and EndSubObject for you automatically. Please see the remarks of Depth about what happens when this method is not called.

This method has six possible outcomes: (1) The request to read/write is approved. In this case, this method returns (true, lengthIfKnown, childKey) and Depth increases by one. childKey is the same reference you passed to this method. (2) You set childKey = null and Mode is not Loading. (also, (mode & (ObjectMode.NotNull | ObjectMode.Deduplicate)) != ObjectMode.NotNull). This indicates that no child object exists, so this method returns (false, _, null). (3) The Mode is Loading and the input stream contains a representation of null, so this method returns (false, _, null). (4) The list/tuple being read/write has already been read/written earlier, and you enabled deduplication, so the request to read/write is declined. In this case, this method returns false with a reference to the object that was loaded or saved earlier. (5) The Mode is Query, Schema or Merge and the current ISyncManager has decided not to traverse into the current field. In this case, this method returns (false, _, childKey). (6) An exception is thrown if the input stream doesn't contain a list/object as expected, or if you're writing a list without giving listLength.

In Saving mode, and in every case except 4, the returned Object is the same as childKey.

Property Documentation

object Loyc.SyncLib.ISyncManager.CurrentObject
set

Sets the "current object" reference. This property must be set when deserializing object graphs with cycles (see remarks).

To understand why this property is needed, consider a Person class that has a reference to all the Siblings of the person:

class Person
{
public string Name;
public int Age;
public Person[] Siblings;
}

If Jack and Jill are siblings then Jack has a reference to Jill, and Jill has a reference back to Jack. A naive implementation of a synchronization function for Person might look like this:

public Person SyncPerson(ISyncManager sync, Person obj)
{
obj ??= new Person();
obj.Name = sync.SyncNullable("Name", obj.Name);
obj.Age = sync.Sync("Age", obj.Age);
obj.Siblings = sync.SyncList("Siblings", obj.Siblings, SyncPerson);
}

But it's impossible for this function to load a Person correctly! To understand why, let's think about what SyncList does: it reads a list of Persons (synchronously), and returns a Person[]. But each Person in that array contains a reference back to the current person. If Jack is being loaded, then the Person[] contains Jill, which has a reference back to Jack.

But SyncList can't return an array that has a reference to Jack, because the reference to Jack only exists in the local variable obj. So as the SyncList method deserializes Jill, Jill's synchronizer must fail while reading Jill's list of siblings.

To fix this, set CurrentObject before calling SyncList:

public Person SyncPerson(ISyncManager sync, Person obj)
{
sync.CurrentObject = obj ??= new Person();
obj.Name = sync.SyncNullable("Name", obj.Name);
obj.Age = sync.Sync("Age", obj.Age);
obj.Siblings = sync.SyncList("Siblings", obj.Siblings, SyncPerson);
}

If the current type cannot contain any objects that may refer back to itself, then setting CurrentObject is optional.

int Loyc.SyncLib.ISyncManager.Depth
get

Returns the number of objects for which BeginSubObject opened an object, list or tuple that was not closed EndSubObject. This property is zero if BeginSubObject was never called to start a subobject.

If you use a helper method designed to write a single object, such as SyncJson.Write{T, SyncObject}(T, SyncObject, SyncJson.Options?) or SyncBinary.Write{T, SyncObject}(T, SyncObject, SyncBinary.Options?), these helper methods call BeginSubObject so the Depth will be 1 inside the synchronizer for your root object.

Typically it is allowed, but not recommended, to write fields at depth 0. For example, this code writes two JSON primitives at depth 0:

<![CDATA[
    var writer = SyncJson.NewWriter();
    writer.Sync("a", 1234.5);
    writer.Sync("b", "Hello\t!");
    var output = (ArrayBufferWriter<byte>) writer.Flush();
    Console.WriteLine(Encoding.UTF8.GetString(output.WrittenSpan));
    Console.WriteLine();
    // Read the data that was just written
    var reader = SyncJson.NewReader(output.WrittenMemory);
    var a = reader.Sync(null, 0.0);
    var b = reader.Sync(null, "");
    Console.WriteLine($"a = {a}, b = {b}");
]]>

The output is pseudo-JSON, formatted like a list without square brackets:

  1234.5,
  "Hello\t!"
  

Officially this is not valid JSON, but as the example shows, it is understood by SyncJson.Reader which successfully reads it back:

  a = 1234.5, b = Hello !
  
bool Loyc.SyncLib.ISyncManager.IsInsideList
get

Indicates that the properties of the current sub-object do not have names because the basic ObjectMode is either ObjectMode.Tuple or ObjectMode.List. In this case, SupportsReordering is irrelevant, since fields do not have names or ID numbers.

bool Loyc.SyncLib.ISyncManager.IsPlainText
get

true if the current format is a plain-text rather than binary format. This property may be used, for example, to decide whether to output a human- friendly encoding of certain data types such as DateTime.

bool Loyc.SyncLib.ISyncManager.IsReading
get

Returns true if the current Mode is SyncMode.Reading, SyncMode.Schema or SyncMode.Merge. If your synchronizer method behaves differently when it is loading than when it is saving, it's often more appropriate to get this property rather than testing whether Mode == SyncMode.Loading, because if the current mode is Schema or Merge, your synchronizer method should do most of its "loading" behavior.

bool Loyc.SyncLib.ISyncManager.IsWriting
get

Returns true if the current Mode is SyncMode.Writing, SyncMode.Query or SyncMode.Merge. If your synchronizer method behaves differently when it is loading than when it is saving, you should almost always get this property rather than testing whether Mode == SyncMode.Saving, because if the current mode is Query or Merge, your synchronizer method should usually do the same thing it does when saving.

int Loyc.SyncLib.ISyncManager.MinimumListLength
get

This is a low-level property; end-users normally should NOT call it! If a variable-length list is being scanned in Loading mode, this property returns either the list length (if known) or the minimum list length (if the total length is not known before reading the list). This property is null in Saving mode, or when a variable-length list is not being scanned.

Some data formats use length-prefixed lists, in which case the list length is known from the begining. Other formats (such as JSON) use a delimiter to mark the end of the list, so the list length is not known until the end is reached. In that case, this property should return 0.

SyncMode Loyc.SyncLib.ISyncManager.Mode
get

Indicates what kind of synchronizer this is: one that saves data, one that loads data, or one that saves a schema.

bool Loyc.SyncLib.ISyncManager.NeedsIntegerIds
get

Indicates that the serialized format uses field ID numbers rather than field names (e.g. Protocol Buffers). When using strings or global symbols, the ID is indicated implcitly via field order: the first field read or written has ID=1, the second has ID=2, etc. If you need to customize the field numbers, you can do so by creating a private Loyc.SymbolPool and creating symbols with custom ID numbers.

FieldId Loyc.SyncLib.ISyncManager.NextField
get

If SupportsNextField is true, the end of the current object has not been reached, and IsInsideList is false, this property returns the name or integer ID of the next field in the input stream. Otherwise, it returns FieldId.Missing.

Even if a data stream supports reordering (SupportsReordering), it may be inefficient to read fields out-of-order. Therefore, if your code wants to read as efficiently as possible, and it expects to receive data out of order, it can use this property to read the fields in the order they appear. Another reason you might read data this way is if you expect to read an extremely large object (e.g. 1 GB or more) and you want to avoid the extra memory allocations that occur when reading data out of order.

Here is an example: public MyObject Sync(ISyncManager sm, MyObject? obj) { obj ??= new MyObject(); if (!sm.SupportsNextField || sm.NeedsIntegerIds) { // Synchronize in the normal way obj.Field1 = sm.Sync("Field1", obj.Field1); obj.Field2 = sm.Sync("Field2", obj.Field2); obj.Field3 = sm.Sync("Field3", obj.Field3); } else { // Synchronize fields in the order they appear in the input. FieldId name; while ((name = sm.NextField) != FieldId.Missing) { if (name.Name == "Field1") { obj.Field1 = sm.Sync(null, obj.Field1); } else if (name.Name == "Field2") { obj.Field2 = sm.Sync(null, obj.Field2); } else if (name.Name == "Field3") { obj.Field3 = sm.Sync(null, obj.Field3); } else { throw new Exception("Unexpected field: " + name.Name); } } } return obj; }

Warning: some readers support name conversion, so that the name passed to the Sync methods is not the same as the name used in the data stream. For example, when SyncJson.Options.NameConverter is SyncJson.ToCamelCase, a name like "Field1" is stored as "field1". But NextField may report the actual string from the datastream ("field1") which will cause code written this way to fail. If the camelcase conversion is the only one you intend to support, you can work around this problem in your synchronizer with uppercase comparisons:

string? name; while ((name = sm.NextField.Name?.ToUpperInvariant()) != null) { if (name == "FIELD1") { obj.Field1 = sm.Sync(null, obj.Field1); } else if (name == "FIELD2") { obj.Field2 = sm.Sync(null, obj.Field2); } else if (name == "FIELD3") { obj.Field3 = sm.Sync(null, obj.Field3); } else { throw new Exception("Unexpected field: " + name); } }

However, this workaround reduces the performance advantage of reading fields in order.

There are three other things worth noticing about this example.

First, this example is only designed to support string field names, so it checks the NeedsIntegerIds property and falls back on the "normal" synchronization style if it is true. You also need a block of "normal" synchronization code when writing an object to an output stream.

Second, notice that this style of reading also allows you to detect unexpected field names and respond to them (in this example, an exception is thrown when an unexpected field is encountered).

Third, notice the use of null field names (sm.Sync(null, ...)). This is how you ask ISyncManager to synchronize the next field without regard for the name of that field.

Another potential use of this property is to save or load a string dictionary: <![CDATA[ public IDictionary<string, string?> Sync( ISyncManager sm, IDictionary<string, string?>? dict) { dict ??= new Dictionary<string, string?>(); if (sm.IsReading) { if (!sm.SupportsNextField || sm.NeedsIntegerIds || sm.IsWriting) throw new NotSupportedException( "StringDictionarySync is incompatible with this " + sm.GetType().Name);

string? name; while ((name = sm.NextField.Name) != null) { dict[name] = sm.Sync(null, ""); } } else { // Writing foreach (var pair in dict) sm.Sync(pair.Key, pair.Value); } return dict; } ]]>

However, loading/storing a dictionary this way is not compatible with data formats that don't use string field names, such as protocol buffers.

ISyncOptions Loyc.SyncLib.ISyncManager.Options
get

Gets the current Options object for this manager (its type is specific to the manager). Note: this is meant for reading options. The manager might not be designed for options to be changed after a sync has started.

bool Loyc.SyncLib.ISyncManager.ReachedEndOfList
get

This is a low-level property that most code should NOT call directly. If a list is being scanned and the current object can detect the end of the list (e.g. the mode is SyncMode.Loading and BeginSubObject returned a length of int.MaxValue), this property returns a boolean value: true at the end of the list and false otherwise. In all other cases, null is returned.

  • In Loading mode, the manager may detect when the end of the list is reached (e.g. in JSON it looks for a closing brace '}'), so true or false is returned if a list is being scanned. However, in certain data formats (e.g. SyncBinary) the data stream uses a length prefix at the beginning of the list and doesn't contain a clear indication when the list ends, so BeginSubObject returns the Length and this property returns null.
  • In Saving mode, the caller knows the list length but the manager does not, so this property always returns null.
  • In Schema mode, no list actually exists, but the manager typically pretends that the list's length is 1.
  • In Query mode, the manager doesn't know the list length but it may have a maximum list length. In this case, this property returns null at the beginning, then true when the limit is reached.
  • In Merge mode... this needs some thought; TODO
bool Loyc.SyncLib.ISyncManager.SupportsDeduplication
get

Returns true if the ISyncManager supports deduplication of objects and cyclic object graphs. Note: all standard implementations of this interface do support deduplication and cyclic object graphs.

bool Loyc.SyncLib.ISyncManager.SupportsNextField
get

Indicates that this implementation of ISyncManager supports the NextField property. (This property must be false when IsReading is false.)

All implementations where SupportsReordering is true should also return true for this property.

bool Loyc.SyncLib.ISyncManager.SupportsReordering
get

Indicates that the serialized format has some kind of schema that enables fields to be read in a different order than they were written (e.g. JSON, Protobuf). If this field is false, fields must be read in the same order they were written, and omitting fields is not allowed (e.g. you cannot skip over a null field without saving it, nor skip over a field and then read it later).

If this property is false, the data may not have any recorded structure, and failure to read the correct fields in the correct order tends to give you "garbage" results.

This property should be false for incomplete reader implementations in which the data format can support reordering physically, but the reader does not.