|
3 | 3 | using System.Diagnostics;
|
4 | 4 | using System.IO;
|
5 | 5 | using System.Linq;
|
| 6 | +using System.Runtime.CompilerServices; |
6 | 7 | using System.Threading.Tasks;
|
7 | 8 | using SpacetimeDB.BSATN;
|
8 | 9 | using SpacetimeDB.ClientApi;
|
9 | 10 |
|
| 11 | +#nullable enable |
10 | 12 | namespace SpacetimeDB
|
11 | 13 | {
|
12 | 14 | public abstract class RemoteBase
|
@@ -50,10 +52,20 @@ public interface IRemoteTableHandle
|
50 | 52 | }
|
51 | 53 |
|
52 | 54 |
|
| 55 | + /// <summary> |
| 56 | + /// Base class for views of remote tables. |
| 57 | + /// </summary> |
| 58 | + /// <typeparam name="EventContext"></typeparam> |
| 59 | + /// <typeparam name="Row"></typeparam> |
53 | 60 | public abstract class RemoteTableHandle<EventContext, Row> : RemoteBase, IRemoteTableHandle
|
54 | 61 | where EventContext : class, IEventContext
|
55 | 62 | where Row : class, IStructuralReadWrite, new()
|
56 | 63 | {
|
| 64 | + // Note: This should really be also parameterized with RowRW: IReadWrite<Row>, but that is a backwards- |
| 65 | + // incompatible change. Instead, we call (IReadWrite<Row>)((IStructuralReadWrite)new Row()).GetSerializer(). |
| 66 | + // Serializer.Read is faster than IStructuralReadWrite.Read<Row> since it's manually monomorphized |
| 67 | + // and therefore avoids using reflection when initializing the row object. |
| 68 | + |
57 | 69 | public abstract class IndexBase<Column>
|
58 | 70 | where Column : IEquatable<Column>
|
59 | 71 | {
|
@@ -134,16 +146,35 @@ public RemoteTableHandle(IDbConnection conn) : base(conn) { }
|
134 | 146 | // THE DATA IN THE TABLE.
|
135 | 147 | // The keys of this map are:
|
136 | 148 | // - Primary keys, if we have them.
|
137 |
| - // - Byte arrays, if we don't. |
| 149 | + // - The entire row itself, if we don't. |
138 | 150 | // But really, the keys are whatever SpacetimeDBClient chooses to give us.
|
139 |
| - // |
140 |
| - // We store the BSATN encodings of objects next to their runtime representation. |
141 |
| - // This is memory-inefficient, but allows us to quickly compare objects when seeing if an update is a "real" |
142 |
| - // update or just a multiplicity change. |
143 | 151 | private readonly MultiDictionary<object, IStructuralReadWrite> Entries = new(EqualityComparer<object>.Default, EqualityComparer<Object>.Default);
|
144 | 152 |
|
| 153 | + private static IReadWrite<Row>? _serializer; |
| 154 | + |
| 155 | + /// <summary> |
| 156 | + /// Serializer for the rows of this table. |
| 157 | + /// </summary> |
| 158 | + private static IReadWrite<Row> Serializer |
| 159 | + { |
| 160 | + get |
| 161 | + { |
| 162 | + // We can't just initialize this statically, because some BitCraft row types have static |
| 163 | + // methods that read SpacetimeDBService.Conn.Db, and these fail if the connection is not |
| 164 | + // there on the first load of those types (????). |
| 165 | + // This should really be considered an error on their part, but for now we delay initializing any Rows until |
| 166 | + // Serializer is actually read, that is, until a row actually needs to be deserialized -- |
| 167 | + // at which point, the connection should be initialized. |
| 168 | + if (_serializer == null) |
| 169 | + { |
| 170 | + _serializer = (IReadWrite<Row>)new Row().GetSerializer(); |
| 171 | + } |
| 172 | + return _serializer; |
| 173 | + } |
| 174 | + } |
| 175 | + |
145 | 176 | // The function to use for decoding a type value.
|
146 |
| - IStructuralReadWrite IRemoteTableHandle.DecodeValue(BinaryReader reader) => IStructuralReadWrite.Read<Row>(reader); |
| 177 | + IStructuralReadWrite IRemoteTableHandle.DecodeValue(BinaryReader reader) => Serializer.Read(reader); |
147 | 178 |
|
148 | 179 | public delegate void RowEventHandler(EventContext context, Row row);
|
149 | 180 | public event RowEventHandler? OnInsert;
|
@@ -304,3 +335,4 @@ void IRemoteTableHandle.PostApply(IEventContext context)
|
304 | 335 | }
|
305 | 336 | }
|
306 | 337 | }
|
| 338 | +#nullable disable |
0 commit comments